Troubleshooting
Common errors and how to fix them — HTTP status codes, SDK error classes, key issues, language pitfalls, and rate limits.
This page is the single source of truth for "my call failed, what now?" Bookmark it.
Quick triage
| Symptom | Most likely cause | Fix |
|---|---|---|
401 Unauthorized | Wrong / missing / revoked API key | Re-mint the key in Studio Keys page |
401 Unauthorized after working before | Org suspended | Check with your admin; suspended orgs revoke all keys |
403 Forbidden | Live key in mobile bundle | Use ohms_test_* for dev, proxy live keys via your backend |
404 Not Found on /extract/:slug | API not published / wrong slug | Click Publish in Studio; verify the slug in the URL |
422 Unprocessable Entity | Required input missing or wrong type | Read error.fields[] for the exact JSON-Schema path |
429 Too Many Requests | Per-key rate limit hit | Back off using error.retryAfterSec |
OHMValidationError thrown | Server returned data that didn't match the SDK's expected shape | Usually a server bug — copy the requestId and contact support |
Empty data object | The transcript didn't mention anything in your schema | Inspect result.transcript — the LLM is faithful, garbage-in = empty-out |
| Transcript is in a non-English script | You're calling the wrong endpoint | audio.transcribe and audio.extract always return English — but extract (text-in) trusts whatever you send it |
Error classes (all SDKs)
Every SDK call that fails throws a typed error. Pattern-match by class:
import {
OHMError, // base class
OHMAuthError, // 401 / 403
OHMValidationError, // 422 — has `fields: string[]`
OHMRateLimitError, // 429 — has `retryAfterSec?: number`
OHMServerError, // 5xx
} from "@ohm_studio/sdk";
try {
await ohm.audio.extract({ apiSlug, file });
} catch (e) {
if (e instanceof OHMRateLimitError) {
await sleep((e.retryAfterSec ?? 1) * 1000);
return retry();
}
if (e instanceof OHMAuthError) {
rotateKey();
return;
}
if (e instanceof OHMValidationError) {
console.warn("schema mismatch at", e.fields);
return showFieldErrors(e.fields);
}
if (e instanceof OHMServerError) {
log.error({ requestId: e.requestId, code: e.code }, "OHM server error");
return showFriendlyError();
}
// Network / timeout / unknown
throw e;
}Every error has:
| Field | Type | Notes |
|---|---|---|
message | string | Human-readable |
code | string | Machine-readable enum (e.g. "INVALID_API_KEY", "RATE_LIMITED") |
status | number | HTTP status |
requestId | string | undefined | Quote this in support tickets — we look it up directly |
requestId is the single thing to copy when contacting support. With it we can pull your exact request from server logs in seconds; without it we're guessing.
"I copied the API key but get 401"
Three things to check, in order:
- Whitespace. Copy-paste sometimes adds a leading newline.
console.log(apiKey.length)— should be a fixed length, no surrounding whitespace. - Mode mismatch.
ohms_test_*keys only hit test-mode endpoints;ohms_live_*only hit live. The SDK forwards whichever you give it. - Wrong project. Each project has its own keys. If you minted the key under "Project A" but the API you're calling lives in "Project B", you'll 401.
If all three are clean, mint a new key — keys can't be "viewed again" after the first reveal, so a copy mistake is unrecoverable. The old key stays valid until you revoke it.
Live keys leaked into a public bundle?
Revoke the key from Studio immediately. The server marks it
revokedAt; the next request from that key returns 401. No one can
reuse it.
"My transcript is in Tamil/Hindi/Telugu — I expected English"
You're probably calling extract (text-only) with the raw STT output from another provider. OHM's audio.transcribe and audio.extract always return English (OHM's STT runs in translate mode), but the text-only extract endpoint trusts whatever you send it.
Two fixes:
- Use
audio.extractinstead of running STT yourself. One call, audio in, English transcript + structured JSON out. - Translate before calling
extractif you must use a custom STT pipeline. The clinical foundation prompt is English-first.
"Structured extraction failed after 3 attempts: No object generated"
This used to happen intermittently when the LLM provider wrapped its
JSON response in markdown code fences. The server now applies the
Vercel AI SDK's extractJsonMiddleware (an official SDK middleware
documented for exactly this case), which strips fences before the JSON
parser runs. If you saw this error before May 2026, it's gone in the
current server build — no client change required.
"Extraction is empty / missing fields"
Three usual causes:
- The fact wasn't in the transcript. OHM's extraction is faithful — it extracts what was actually said, not what should have been said. Open the Studio Playground, paste the transcript, and watch which fields populate. If they're empty in Playground too, the LLM is correctly omitting them.
- The schema field doesn't match the doctor's wording. Add a
helpTextto the field describing how the speaker phrases it ("e.g. 'BP one fifty by ninety'"). The Foundation Block uses field labels + helpText to route facts. - Foundation Block was disabled for this API. Check Studio → API → Settings. The opt-out is per-API; un-toggle to restore the clinical safety priors.
Rate limits
Per-key rate limits are configurable in Studio → Project → Keys. Default is 60 requests/minute per key. On 429, the SDK throws OHMRateLimitError with retryAfterSec — back off for at least that long.
For sustained higher throughput, mint multiple keys (one per worker) or contact support to raise the limit.
"The Studio Playground works but the SDK call fails"
Almost always means the API hasn't been published since your last edit. Studio Playground runs against the draft spec; the SDK calls the published snapshot. Click Publish in the API builder.
Less commonly:
- The published key is from a different project than the one you're calling.
- You renamed the API slug after publishing (the old slug is gone).
"ohms_live_ keys are blocked in my React Native app"
That's the SDK protecting you. Live keys in a mobile bundle are extractable by anyone who downloads the IPA / APK. Two production patterns:
- Backend proxy (recommended). Your RN app calls your backend with a session token; your backend calls OHM with the live key. See RN Key Handling for the full pattern.
- Test-mode keys for development.
ohms_test_*keys are bundle-safe — they hit test endpoints with no PHI risk. Use them in sandbox builds and for local development.
If you really need to bundle a live key (e.g. an internal-only tablet kiosk), pass acknowledgeBundledKey: true to the constructor — but consider this a code smell.
Still stuck?
Email support@ohm.doctor with:
- The
requestIdfrom the error (single most important thing) - The SDK package + version (
@ohm_studio/sdk@x.y.z) - A minimal reproduction (cURL + redacted transcript is enough)
We respond within one business day for paid orgs, two for free.