D-ID Client SDK - v3.0.9
    Preparing search index...

    Handling errors

    Every error the SDK raises is a BaseError carrying a kind, and isDIDError is the guard that recognizes one. Inside that guard a switch on kind narrows the value to a single class and its own fields, such as status and code on an HttpError or online on a NetworkError.

    DIDError is the seven classes isDIDError narrows to:

    kind Class Raised when
    'HttpError' HttpError A request to the Agents API came back non-2xx.
    'NetworkError' NetworkError A request never reached the server.
    'StreamError' StreamError The media stream itself failed.
    'WSError' WsError The notifications web socket failed.
    'ValidationError' ValidationError An argument, or the state the manager is in, does not allow the call.
    'ChatCreationFailed' ChatCreationFailed The Agents API answered a chat-creation request without a chat.
    'ChatModeDowngraded' ChatModeDowngraded The server created the chat in a ChatMode without video.

    Note that WsError's kind is 'WSError', not 'WsError'. BaseError itself is deliberately not a member of the union: its kind is a plain string, which matches every literal and would stop the narrowing from working.

    import { isDIDError } from '@d-id/client-sdk';

    function describe(error: unknown): string {
    if (!isDIDError(error)) {
    throw error; // Not ours — do not swallow it.
    }

    switch (error.kind) {
    case 'HttpError':
    // `status` is the transport-level branch, `code` the Agents API's own classification.
    return error.code === 'InsufficientCreditsError'
    ? 'This account is out of credits.'
    : `The agent service answered ${error.status}.`;
    case 'NetworkError':
    return error.online === false
    ? 'You appear to be offline.'
    : `Could not reach ${error.endpoint ?? 'the agent service'}.`;
    case 'StreamError':
    case 'WSError':
    return 'The connection to the agent dropped.';
    case 'ValidationError':
    return error.message;
    case 'ChatCreationFailed':
    return 'The conversation could not be started.';
    case 'ChatModeDowngraded':
    return 'The agent is running in a limited mode.';
    default:
    // Total on purpose: a value from another copy of the SDK can land here.
    return 'Something went wrong.';
    }
    }

    error.kind alone is enough for the switch; instanceof HttpError is not, because two copies of the SDK in one bundle define two classes and neither is instanceof the other.

    status is the HTTP status. code is the Agents API's own classification, read from D-ID's { kind, description } error body, and 'HttpError' when the response was not that envelope. It is a plain string because the set belongs to the API and grows without an SDK release, so branch on the codes you care about and fall through on the rest.

    if (isDIDError(error) && error.kind === 'HttpError') {
    if (error.code === 'InsufficientCreditsError') {
    showTopUpDialog();
    } else if (error.status === 401 || error.status === 403) {
    // A client key that is not authorized for this agent or this domain.
    refreshCredentials();
    }
    }

    An agent that is still being built answers with 409 and code 'NotReadyError', and is ready in a minute or two. connect() does not retry it and does not switch the session to Maintenance. To connect after the build, create a new manager with createAgentManager: a manager reads the agent once, when it is created, and the build can change how the agent streams. One whose build failed or was rejected answers with 409 and code 'ConflictError'; connect() does not retry that either.

    The server's classification moved from kind to code in 3.0 — see the migration guide.

    Rejected to the caller. ValidationError and ChatCreationFailed are thrown to whoever called the method — chat(), speak(), rate(), deleteRate(), submitFeedback(), changeMode(), connect(), reconnect(), the Expressive-only media methods, and createAgentManager itself. They never reach onError. A ValidationError is a programming or state error: a message that is empty or too long, a call made before connect(), a second connect() on an open session, a mode the agent does not support.

    Delivered to onError. WsError, StreamError and ChatModeDowngraded are never thrown. That includes a call that caused one: on an Expressive (V4) agent, a chat() whose data-channel send fails still resolves, and the failure arrives only here as a StreamError.

    Both. HttpError and NetworkError are handed to onError and rejected by the method that made the request, so either place can handle them. Two caveats: for the message-send request behind chat() the callback may not fire, though the error is still thrown; and connect() makes up to three attempts, each limited to 45 seconds, before it rejects. It does not retry a 429, a 409 or an out-of-credits response, and each failed attempt reaches onError, so one failed connect() can report up to three times. When the last attempt times out, the rejection is a plain Error, not an SDK error.

    The practical split is to catch around the call for anything with a UI consequence at that point, and to let onError feed the error reporter.

    toJson() produces the JSON-safe ErrorJson payload, and it is what to send to a logging service. It carries only the fields the SDK deliberately exposes: never the request body, which holds the end user's own message, and never a non-Error cause, which could be an arbitrary object carrying credentials. A cause that is an Error contributes its message, truncated, and only when it says something the message does not.

    The second argument of onError is an ErrorContext — endpoint, method, sessionId and streamId, all optional — saying where the failure happened. Attach it alongside:

    import { isDIDError, type AgentManagerCallbacks } from '@d-id/client-sdk';

    const onError: AgentManagerCallbacks['onError'] = (error, errorData) => {
    reportToYourErrorService({
    ...(isDIDError(error) ? error.toJson() : { kind: 'Error', message: error.message }),
    ...errorData,
    });
    };

    An HttpError's payload adds code, httpStatus and, when the failing call is known, endpoint and method; a NetworkError's adds endpoint, method, durationMs, online and visibility, which between them usually say whether the request ever left the browser. ErrorJson's index signature is typed unknown, so a key it does not declare has to be narrowed before it is used.

    import * as sdk from '@d-id/client-sdk';
    import { isDIDError } from '@d-id/client-sdk';

    const agentManager = await sdk.createAgentManager('agt_fumf1234', {
    auth: { type: 'key', clientKey: 'YOUR_CLIENT_KEY' },
    callbacks: {
    onSrcObjectReady(value) {
    videoElement.srcObject = value;
    },
    onError(error, errorData) {
    reportToYourErrorService({
    ...(isDIDError(error) ? error.toJson() : { kind: 'Error', message: error.message }),
    ...errorData,
    });

    if (isDIDError(error) && error.kind === 'ChatModeDowngraded') {
    showBanner('The agent is running in a limited mode.');
    }
    },
    },
    });

    try {
    await agentManager.connect();
    await agentManager.chat('What is the distance to the moon?');
    } catch (error) {
    // ValidationError and ChatCreationFailed only ever arrive here.
    showBanner(describe(error));
    }