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.The phase tells you when the error happened — the same code can mean a failed start or a dropped connection mid-session:
| Phase | When |
|---|---|
| start | Creating the session, opening its connections, or starting the microphone. |
| recover | Re-establishing a session Speechineer had dropped. |
| connection | A connection failed while the session was running. |
| action | A request you made was rejected. |
| end | Finishing the session. |
| runtime | Speechineer stopped the session; it cannot continue and recoverable is false. |
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, error.recoverable), });tsreadonly voice = injectSpeechToForm({ form: intakeForm, onError: (error) => this.showBanner(error.code, error.recoverable), });tsconst session = speechineer.speechToForm({ form: intakeForm, onError: (error) => showBanner(error.code, error.recoverable), }); - 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, error.recoverable); } };tsasync speak() { try { await this.voice.start(); } catch (error) { this.showBanner(error.code, error.recoverable); } }tsspeak.onclick = async () => { try { await session.start(); } catch (error) { showBanner(error.code, error.recoverable); } };
Error codes
Grouped by what you should do about them. The codes are frozen: they will not change under you.
In the browser
Raised by the SDK itself. 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). Recoverable — ask again. | Serve over HTTPS or localhost and surface the browser's permission prompt again. error.recoverable is true — 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). Recoverable. | 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. |
Authentication
Fix the auth token or key configuration; do not retry unchanged.
| Code | Meaning | Possible fix |
|---|---|---|
| AUTH_REQUIRED | No auth token was presented. | Pass the API key to createClient — or, in Signed mode, make sure your token endpoint returns a token. |
| 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
Surface to the user or your operations; retrying will not help until limits change.
| 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. |
| 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. |
Form and workflow resolution
Check the form key, version, language, and workflow configuration.
| 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. |
| 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. |
| WORKFLOW_NOT_FOUND | The requested workflow does not exist. | Check the workflow name; leave it unset to get the default. |
| WORKFLOW_STANDALONE_MODE_UNSUPPORTED | This workflow has no standalone mode. | Use a workspace form for this workflow; inline definitions are not supported here. |
| SESSION_NOT_FOUND | The workflow you tried to resume no longer exists. | Start a new session instead of resuming; the previous one has expired. |
Lifecycle
The component is deactivated or archived — resolve in your workspace.
| 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. |
| COMPONENT_ARCHIVED | A write was attempted on an archived component. | Archived is final. Create a new form, key or account and point your code at it. |