D-ID Client SDK - v3.0.0-0
    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 — status and code on an HttpError, key on a ValidationError. This guide covers the union, the two places a failure can surface, and what to log.

    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 handled the session in a narrower ChatMode than the one asked for.

    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. That is exactly the case isDIDError is built for.

    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();
    }
    }

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

    A failure surfaces in one of two places, and which one it is depends on the error, not on the call.

    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 arrive only there — they have no call to reject, because nothing the application invoked caused them.

    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() retries the initialization up to three times before its failure surfaces at all, except on a 429, on an out-of-credits response, and when the transport reports that it could not connect at all.

    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));
    }