Integrate the SDK
Errors
When something fails in a session — while it starts, while it runs, or when a request you made is rejected — it reaches your code as one typed error. You catch it, explain it to the user in your own words, and branch on it; every code it can carry is listed below.
The error object
Every error is one object:
Tip
error.code, never on the wording of error.message, which may change.Categories and classes
Every error also belongs to a category, and each category has its own class. Catch the class to handle a whole family at once — SpeechineerQuotaError covers every way an allowance can run out — or read error.code when you need the specific cause:
| Category | Class | When |
|---|---|---|
| client | SpeechineerClientError | Raised by the SDK itself — nothing was sent. Fix locally; most are worth a retry button. |
| auth | SpeechineerAuthError | A credential travelled and was refused. Fix the token or key configuration; do not retry unchanged. |
| quota | SpeechineerQuotaError | An allowance is spent. Surface it to the user or your operations; retrying will not help until limits change. error.meta carries the numbers — see below. |
| access | SpeechineerAccessError | The credentials are valid but this workspace, organization or account may not do it — nothing on the page side changes it. |
| not_found | SpeechineerNotFoundError | Something the request names does not exist. Check the key, version and language — retrying unchanged never helps. |
| gone | SpeechineerSessionEndedError | It existed and has ended. Start a new one — this is the only category that can never be retried. |
| conflict | SpeechineerConflictError | The request is well formed but disagrees with how the thing it names is already set up. |
| validation | SpeechineerValidationError | Speechineer understood the request and refused its content. error.details lists one entry per offending field, and error.field names it when there is only one. |
| rate_limit | SpeechineerRateLimitError | Nothing is spent — you are going faster than allowed. Retrying later is the fix. |
| runtime | SpeechineerRuntimeError | The session was live and could not continue. It is over — start a new one. |
| service | SpeechineerServiceError | Ours, not yours. Retrying later is reasonable; tell us the request id if it persists. |
| upstream | SpeechineerUpstreamError | Something Speechineer relies on replied with something unusable. Retrying later is reasonable. |
| unavailable | SpeechineerUnavailableError | Nothing answered at all. Retrying later is reasonable. |
| timeout | SpeechineerTimeoutError | The work may or may not have happened. Retry only what is safe to repeat. |
The numbers on a quota error
A quota error is the one category that carries figures, on error.meta. The keys are the same whether the allowance ran out on the request that started the session or while it was running, so one piece of code renders both:
| Key | What it holds |
|---|---|
| limit | The ceiling that bound, in units. |
| used | How much of it was already consumed. |
| remaining | What was left — 0 whenever the error fired. |
| layer | Which allowance bound, as `<scope>:<period>` — e.g. `account:month`. This is what tells a spent account apart from a spent workspace. |
Listen for errors
However a session fails, you never have to hunt for the error: the same object is delivered to three places, each suited to a different job — a callback for reacting the moment something fails, the session state for rendering, and the rejected promise for the code that asked. Use whichever fits the code you are writing.
- The
onErrorcallback — called the moment it happens. Log the error or send it to analytics from there:tsconst voice = useSpeechToForm({ form: intakeForm, onError: (error) => showBanner(error.code), });tsreadonly voice = injectSpeechToForm({ form: intakeForm, onError: (error) => this.showBanner(error.code), });tsconst session = speechineer.speechToForm({ form: intakeForm, onError: (error) => showBanner(error.code), }); - The session state — the error sits there as
errorand stays until the nextstart()succeeds. Render your message from there —describeis your own mapping from a code to your wording:tsx{voice.error && <p role="alert">{describe(voice.error.code)}</p>}html@if (voice.error(); as error) { <p role="alert">{{ describe(error.code) }}</p> }tssession.subscribe((state) => { errorEl.hidden = !state.error; errorEl.textContent = state.error ? describe(state.error.code) : ""; }); - The rejected promise —
start()andend()reject with the same error, so anawaitwhere you call them catches it directly:tsxconst onSpeak = async () => { try { await voice.start(); } catch (error) { showBanner(error.code); } };tsasync speak() { try { await this.voice.start(); } catch (error) { this.showBanner(error.code); } }tsspeak.onclick = async () => { try { await session.start(); } catch (error) { showBanner(error.code); } };
Error codes
One section per category, so a section is exactly what one catch gives you: the class in the heading is what instanceof matches, and the codes under it are every way that class can arrive. The codes are frozen — they will not change under you.
In the browser
SpeechineerClientError · client — Raised by the SDK itself — nothing was sent. Fix locally; most are worth a retry button.
| Code | Meaning | Possible fix |
|---|---|---|
| MICROPHONE_DENIED | Microphone access was blocked, or the page is not on a secure origin (HTTPS or localhost). Worth asking again. | Serve over HTTPS or localhost and surface the browser's permission prompt again — offer a retry button. |
| MICROPHONE_UNAVAILABLE | No usable microphone was found. | Ask the user to plug in or enable a microphone, then retry; check the browser's device permissions. |
| AUDIO_UNSUPPORTED | This browser cannot record audio in the required format. | Ask the user to switch to a current browser; no configuration on your side changes this. |
| NETWORK | The request never got an answer — connectivity, or the browser blocked the request (check its console). | If the browser console shows a blocked cross-origin request, your origin was refused — check baseUrl for typos and tell us the origin you serve from. Otherwise check connectivity and retry. |
| NO_SESSION | An action was called before start(). | Call start() before stop(), end() or any other action, or guard the action on the session's state. |
| NO_CLIENT | No client is provided above this component — the provider / provide call is missing. | Wrap the component in the provider (React), call the provide function at bootstrap (Angular), or pass the client explicitly. |
| NO_AUTH | The SDK had no credentials to send — nothing reached Speechineer. Distinct from AUTH_REQUIRED, which means a credential was sent and refused. | Pass the API key to createClient — or, in Signed mode, make sure your token endpoint returns a token. |
| NO_ACCOUNT | An account is required alongside an API key, and none was given. | Pass account to createClient, or to the session when it differs per user. |
| UNKNOWN | Something failed in a way the SDK could not name — the last resort, not a category. | Read error.cause for what actually threw, and tell us the request id if one is set. |
Authentication
SpeechineerAuthError · auth — A credential travelled and was refused. Fix the token or key configuration; do not retry unchanged.
| Code | Meaning | Possible fix |
|---|---|---|
| AUTH_REQUIRED | A credential was presented and Speechineer refused it — missing, malformed or expired. | Check the token has not expired and is minted for the right workspace; re-mint and retry once. |
| API_KEY_INVALID | The API key in the token is unknown or inactive. | Check the key against API keys in your workspace; create a new one if it was deactivated or archived. |
| AUTH_SIGNATURE_REQUIRED | The workspace or key requires a PS256-signed token; alg=none was presented. | This page needs a Signed key: mint PS256 tokens in your backend with a signing key, or use an Unsigned key only where the workspace allows it. |
| AUTH_MODE_MISMATCH | The token's alg does not match the API key's auth mode (Signed keys accept only PS256; Unsigned keys only alg=none). | Check which client the page resolves and which mode its key carries — one app can hold both (see Authentication). |
| AUTH_ENVELOPE_INVALID | The token is malformed, or its claims are missing or wrong. | Compare the claims your backend signs with the reference set (api_key, account_key, aud, iat, exp, jti) and the alg / kid header. |
| AUTH_ENVELOPE_REPLAYED | This token was already used (replayed `jti`). | Mint a fresh token per session start with a unique jti; never cache a token across sessions. |
| AUTH_ENVELOPE_LIFETIME_EXCEEDED | The token is valid for longer than the maximum allowed lifetime. | Shorten exp − iat in your backend; a few minutes is plenty, the SDK asks for a new token every start. |
| ACCOUNT_REQUIRED | The token does not name an account. | Add the account claim to the token (Signed) or pass account.key to createClient (Unsigned). |
| SIGNING_KEY_NOT_FOUND | The token's key id matches no active signing key in the workspace. | Put the signing key's id in the token's kid header, and check the key is still active in Signing keys. |
Quotas and billing
SpeechineerQuotaError · quota — An allowance is spent. Surface it to the user or your operations; retrying will not help until limits change. error.meta carries the numbers — see below.
| Code | Meaning | Possible fix |
|---|---|---|
| API_KEY_QUOTA_EXCEEDED | The API key's units limit is reached. | Raise or remove the key's units limit in its settings, or wait for the period to roll over. |
| WORKSPACE_QUOTA_EXCEEDED | The workspace units limit is reached. | Raise the workspace limit in its settings, or wait for the period to roll over. |
| ACCOUNT_QUOTA_EXCEEDED | The account's units limit is reached. | Raise the account's limit from your backend or in Accounts; tell the user their allowance is used up. |
| FORM_QUOTA_EXCEEDED | The form's units limit is reached. | Raise the form's units limit in its settings, or wait for the period to roll over. |
| PLAN_ALLOWANCE_EXHAUSTED | Included units are used up and no prepaid units remain. | Buy prepaid units or wait for the next cycle; the dashboard shows what remains. |
| OVERSPEND_LIMIT_REACHED | The configured overspend cap is reached. | Raise the overspend cap in Billing, or wait for the next cycle. |
| QUOTA_EXCEEDED | An allowance ran out while the session was running, so the session ended there. Which allowance it was is in error.meta.layer. | Raise the limit that bound, then start a new session; the one that ended cannot be resumed. |
Not allowed
SpeechineerAccessError · access — The credentials are valid but this workspace, organization or account may not do it — nothing on the page side changes it.
| Code | Meaning | Possible fix |
|---|---|---|
| WORKSPACE_INACTIVE | The workspace is deactivated or archived. | Reactivate the workspace in its settings; an archived workspace cannot be brought back — create a new one. |
| CLIENT_INACTIVE | The owning organization is deactivated. | Contact your organization's owner; nothing on the page side fixes this. |
| ACCOUNT_INACTIVE | The end user's account is deactivated or archived. | Reactivate the account in Accounts, or point the session at a different one — an archived account is final. |
| FEATURE_NOT_AVAILABLE | The plan does not include the capability this session asked for. | Check what your plan covers in Billing; ask us to enable the capability if you need it. |
Named but not there
SpeechineerNotFoundError · not_found — Something the request names does not exist. Check the key, version and language — retrying unchanged never helps.
| Code | Meaning | Possible fix |
|---|---|---|
| FORM_NOT_FOUND | No form with this key in the workspace. | Compare form.key with the key shown on the form's page; a key belongs to one workspace only. |
| FORM_VERSION_NOT_PUBLISHED | The version your code names exists but is a draft. | Publish it from the form's header (Publish this version), or point version at the published one. |
| NO_PUBLISHED_VERSION_FOR_LANGUAGE | No published version exists for the requested language. | Add the language to the form and publish, or request a language the published version supports. |
| WORKFLOW_NOT_FOUND | The requested workflow does not exist. | Check the workflow name; leave it unset to get the default. |
| NOT_FOUND | Something the request names does not exist, where no more specific code fits. | Check every key the call passes against what your workspace holds. |
| WORKFLOW_FEATURE_UNKNOWN | The workflow does not offer the capability the session asked for. | Check which capability you start against what the workflow supports. |
| ADAPTER_CONFIG_NOT_FOUND | A model this session pins by name is not registered for your workspace. | Check the model keys you pass; leave them unset to use the ones the form already names. |
| SESSION_NOT_FOUND | The workflow you tried to resume no longer exists. | Start a new session instead of resuming; the previous one has expired. |
The session is over
SpeechineerSessionEndedError · gone — It existed and has ended. Start a new one — this is the only category that can never be retried.
| Code | Meaning | Possible fix |
|---|---|---|
| SESSION_ENDED | The session existed and is over — every call against it answers this from then on. What ended it is the first entry of error.details, so a spent allowance still reads as one. | Start a new session; this one cannot be resumed. If what ended it was a quota, raise the limit first. |
Conflicts with what exists
SpeechineerConflictError · conflict — The request is well formed but disagrees with how the thing it names is already set up.
| Code | Meaning | Possible fix |
|---|---|---|
| FORM_KEY_PORTAL_OWNED | This form is configured in Speechineer — use source: "workspace" for it. | Name the form with source: "workspace" instead of passing an inline definition under that key. |
| COMPONENT_IN_USE | The thing you tried to remove is still referenced by something live. | Detach or archive what still points at it, then retry. |
The request was not acceptable
SpeechineerValidationError · validation — Speechineer understood the request and refused its content. error.details lists one entry per offending field, and error.field names it when there is only one.
| Code | Meaning | Possible fix |
|---|---|---|
| VALIDATION_ERROR | One or more fields of the request are missing or out of range. | Read error.details — each entry names the field and what was wrong with it. |
| FORM_FIELD_INVALID | A field in an inline form definition is not valid for its type. | Check the field's type against what it carries — a slider needs a range, a select needs options. |
| CONFIG_INVALID | This workflow's configuration is incomplete or invalid. | Check the model keys and prompts the session pins; an inline form must supply everything the models it selects require. |
| WORKFLOW_STANDALONE_MODE_UNSUPPORTED | This workflow has no standalone mode. | Use a workspace form for this workflow; inline definitions are not supported here. |
| ADAPTER_CONFIG_NOT_ALLOWED | A model was pinned for a capability this workflow does not have. | Pin models only for the capabilities the workflow actually runs. |
| ADAPTER_CONFIG_NOT_ENTITLED | Your plan does not include the model this session pins. | Choose a model your plan covers, or ask us to enable it. |
| FORM_VERSION_LANGUAGE_UNAVAILABLE | The published version does not carry the language the session asked for. | Add the language to the form and publish it, or ask for one the version already has. |
| FEATURE_CONFIG_INVALID | The capability's configuration is not valid for this form. | Read error.details — each entry names the setting and what was wrong with it. |
| FEATURE_FIELD_CONFIG_REQUIRED | A field is configured in code but the call did not supply its configuration. | Pass fieldConfigs for every field the form leaves open for code. |
| FEATURE_SETTING_NOT_ALLOWED | A setting the call passes may not be set from code for this form. | Drop the setting — the form's configuration owns it. |
| FEATURE_SETTING_INVALID | A setting the call passes is outside the values it accepts. | Read error.details for the accepted range, and pass a value inside it. |
| WEBSOCKET_KIND_UNSUPPORTED | A connection was opened that this session does not use. The session keeps running; only that connection was refused. | Open only the connections the capability you started uses — the session answer lists them. |
Too much at once
SpeechineerRateLimitError · rate_limit — Nothing is spent — you are going faster than allowed. Retrying later is the fix.
| Code | Meaning | Possible fix |
|---|---|---|
| CONCURRENCY_LIMIT_REACHED | Too many workflows are running concurrently. | End sessions you no longer need, queue starts on your side, or ask for a higher concurrency limit. |
A running session stopped
SpeechineerRuntimeError · runtime — The session was live and could not continue. It is over — start a new one.
| Code | Meaning | Possible fix |
|---|---|---|
| TRANSCRIPTION_FAILED | Transcription stopped and could not be restarted. | Start a new session. If it recurs on one form, try its prompts in the Engineer tab. |
| EXTRACTION_FAILED | Field extraction stopped and could not be restarted. | Start a new session. If it recurs on one form, try its prompts in the Engineer tab. |
Speechineer could not answer
SpeechineerServiceError · service — Ours, not yours. Retrying later is reasonable; tell us the request id if it persists.
| Code | Meaning | Possible fix |
|---|---|---|
| INTERNAL_ERROR | Something failed on our side that we did not anticipate. | Retry once; if it persists, send us error.requestId. |
A dependency answered badly
SpeechineerUpstreamError · upstream — Something Speechineer relies on replied with something unusable. Retrying later is reasonable.
| Code | Meaning | Possible fix |
|---|---|---|
| UPSTREAM_INVALID | A service Speechineer depends on returned an answer it could not use. | Retry; if it persists, send us error.requestId. |
A dependency could not be reached
SpeechineerUnavailableError · unavailable — Nothing answered at all. Retrying later is reasonable.
| Code | Meaning | Possible fix |
|---|---|---|
| SERVICE_UNAVAILABLE | A service Speechineer depends on could not be reached. | Retry with a short backoff; if it persists, send us error.requestId. |
A dependency took too long
SpeechineerTimeoutError · timeout — The work may or may not have happened. Retry only what is safe to repeat.
| Code | Meaning | Possible fix |
|---|---|---|
| ACTION_TIMEOUT | An action did not complete within the time allowed for it. | Retry the action; if it times out repeatedly, send us error.requestId. |