Skip to main content
Corti uses standard HTTP status codes. 4xx errors indicate a problem with the request; 5xx errors indicate a problem on Corti’s side. Error responses are in JSON and differ between the SDKs and the REST API. The tabs below cover each.
Some Corti products handle errors and authentication differently than what is described below:
  • Admin API — Separate from the Corti API used for speech to text, text generation, and agentic workflows. See the Administration API reference for details. Please contact us if you have interest in this functionality or further questions.
  • Agents API — The JSON format of the error response is different. See the Agents API docs for more info.
  • Embedded Assistant — Authentication is handled differently. See the Embedded Assistant Authentication docs for those methods.
The SDK throws typed error classes on any non-2xx response or internal failure. Each class exposes a different set of attributes. See Error Handling for full details.

Error Classes

ClassThrown whenAttributes
CortiErrorAPI returned a non-2xx HTTP response (4xx / 5xx)message, statusCode, body, rawResponse
CortiSDKErrorSDK infrastructure error (e.g. localStorage unavailable)message, code, cause
ParseErrorInput validation failed (e.g. missing PKCE verifier, invalid JWT)message, errors
JsonErrorResponse body could not be parsed as JSONmessage, errors

HTTP Status Codes

Error codeWhen it occursHow to resolve
bad_requestThe request body or query parameters are malformed or missing required fields.Double check the fields submitted to be sure of correct format and necessary fields.
Token endpoint errors are returned as JSON in the response body. Corti API errors are returned in the WWW-Authenticate response header with no body. The header is not accessible from the exception directly; use .WithRawResponse() on the request if you need it.
Error codeWhen it occursHow to resolve
invalid_clientClient authentication failed. There is a problem with the client ID.Verify your client_id is correct.
unauthorized_clientClient authentication failed. There is a problem with the client secret.Verify your client_secret is correct.
invalid_tokenThe bearer token is invalid or has expired. Check the WWW-Authenticate header for error_description.Re-authenticate to obtain a fresh token and retry the request.
When it occursHow to resolve
Accessing a resource (interaction, recording, transcript, document) that belongs to a different client.Verify the resource ID was created by the authenticated client.
ErrorWhen it occursHow to resolve
Invalid '{resource_name}' '{resource_id}'The requested resource does not exist.Verify the ID and parent resource ID is correct and was created under the authenticated tenant.
Attempting to access a resource that belongs to a different OAuth client also returns a 404, not a 403. This is intentional. Returning a 403 would confirm that the resource exists, which is a security risk.
You have exceeded the rate limit for the API. The SDK automatically retries with exponential backoff. The default retry limit is 2. Override per request:
const response = await client.interactions.create(..., {
    maxRetries: 0, // disable retries for this request
});
If the error persists, reduce request frequency or contact support to discuss your rate limit requirements.
An unexpected error occurred on the Corti side. Double check the request format and retry the request. If the issue persists, contact support.
The service is temporarily unavailable. Retry with exponential backoff. Monitor the status page.

WebSocket APIs

WebSocket errors from /streams and /transcribe are surfaced as plain Error objects whose message is the server status code (e.g. CONFIG_DENIED, CONFIG_MISSING, CONFIG_TIMEOUT).
Every CONFIG_* server message also carries a reason field with human-readable details behind the failure. Whether you can read it depends on the mode you connected in:
  • Default mode (client.stream.connect({ id, configuration }) / client.transcribe.connect({ configuration })): the SDK handles the handshake-phase and CONFIG_* messages internally. It rejects connect() with just the bare code. The reason is not exposed.
  • awaitConfiguration: false or manual mode: the raw message, including msg.reason, flows through socket.on('message', …).
You sent a config message, but it’s invalid.
reason from serverLikely causeFix
invalid language code: must be a valid BCP 47 language tagtranscription.primaryLanguage is not a supported code.Use a supported language code.
invalid output locale: language should not be emptymode.type: "facts" without mode.outputLocale.Add outputLocale.
invalid output locale: invalid language code: must be a valid BCP 47 language tagmode.type: "facts" with invalid mode.outputLocale code.Use a supported language code
unknown modemode.type is not "transcription" or "facts".Use one of the two supported values.
invalid participantsParticipant role outside doctor / patient / multiple.Use one of the three supported roles.
invalid audio format: MIME type not allowed...audioFormat MIME type is not in the supported list.Use a supported MIME type or omit the field.
You sent a non-config frame before any valid config message arrived.
Only reachable when you opt out of the automatic handshake. Default mode sends config for you.
reason from serverCauseFix
No valid configuration provided for interactionA non-config frame reached the server before a config message. Typically sendAudio() / sendFlush() / sendEnd() in manual mode, or a raw socket.send() queued before OPEN in awaitConfiguration: false.Be sure to call sendConfiguration() immediately after waitForOpen(), or pass configuration to connect() and let the SDK handle the handshake.`
The 10-second handshake window elapsed without any config message arriving.
Only reachable in manual mode. The default handshake sends config well within the 10s budget.
reason from serverCauseFix
Manual mode: no configuration provided within 10sServer’s 10s handshake timer expired.Call sendConfiguration() immediately after waitForOpen().