---
audience: developers
summary: "First-day problems and fixes \u2014 credentials, auth backend, common 4xx\
  \ envelopes."
title: Getting started
path: troubleshooting/getting-started
status: published
---


# Troubleshooting — getting started

The most common problems hit during a first integration, drawn from real alpha bring-up. Search by symptom (the heading) or by the error code in the response.

## "Backend OAuth not configured" / 503 from `/admin/oauth/login`

**Symptom** — clicking "Sign in with ScaiKey" on the admin UI gives a generic error; backend logs show `503 admin OAuth not configured; missing: SCAISCRIBE_SCAIKEY_CLIENT_ID, ...`.

**Cause** — your ScaiScribe deployment hasn't finished its ScaiKey registration. The backend can't initiate the OAuth flow without a client_id + client_secret + session_secret.

**Fix** — your operator needs to register ScaiScribe with ScaiKey (one-time ceremony) and deploy the credentials. After they restart the backend, `/v1/admin/config` returns `{ "auth_configured": true }` and the login button works.

## 401 `UNAUTHENTICATED` from every endpoint

**Symptom** — every SDK call raises `AuthError` (or its language equivalent); the error envelope shows `code: UNAUTHENTICATED`.

**Cause** — either the token is missing/malformed, or ScaiScribe's auth backend doesn't trust the issuer.

**Fix** —
1. Confirm the token is actually being sent: turn on the SDK's debug logging, or check the response's `request_id` against the backend log line for that request.
2. Confirm you're getting the token from the **right ScaiKey deployment**. The token issuer (`iss` claim) must match what ScaiScribe is configured to trust (`SCAISCRIBE_SCAIKEY_BASE_URL`).
3. If the token works against `/v1/health` (which is public) but fails on `/v1/documents`, the token is reaching the backend fine but JWT verification is failing. Common cause: backend points at the wrong JWKS URL. The correct path for ScaiKey global apps is `/api/v1/platform/.well-known/jwks.json` — not `/.well-known/jwks.json`.

## 403 `QUOTA_EXCEEDED`

**Symptom** — calls fail with `QuotaError` / `QuotaException`; envelope shows `code: QUOTA_EXCEEDED` and `details.limit`/`details.current`.

**Cause** — your tenant has hit its render or storage quota.

**Fix** — ask your operator to bump the quota for your tenant. In the meantime, finished documents you `delete_document(doc_id)` count against the quota until the lifecycle worker garbage-collects them; deleting old drafts can free up headroom immediately.

## 403 with no `QUOTA_EXCEEDED` — usually scope-related

**Symptom** — `ForbiddenError`; envelope code varies (`ROLE_INSUFFICIENT`, `TENANT_SCOPE_VIOLATION`, etc.).

**Cause** — your token's scopes don't authorize the endpoint, or you're acting across tenant boundaries you don't have access to.

**Fix** —
1. For admin endpoints (`/v1/admin/*`), verify your user has the right role. For the alpha, only `superadmin` can see global views; `tenant_admin` is scoped to one tenant. Check with `client.admin.me()`.
2. For document endpoints, verify the document's `tenant_id` matches your token's tenant.

## 404 `DOC_NOT_FOUND` when you just created the document

**Symptom** — `create_document` returns a `doc_id`, but the next call (`get_document`, `add_element`, etc.) returns 404.

**Cause** — almost always a typo or stringification bug in the integration code. Document IDs are opaque strings (`doc_01ABC...`); a missing character will silently 404.

**Fix** — log the `doc_id` you got back from `create_document` and the one you're passing in subsequent calls. If they're identical: confirm you're using the **same** ScaiScribe host on both calls. In multi-environment setups it's easy to point at `dev` for create and `prod` for read.

## 422 `VALIDATION_FAILED` on a `PATCH`

**Symptom** — your `add_element` op returns 422; `details` shows a field path and a constraint.

**Cause** — the element payload doesn't match the spec for that element type. Common offenders:

- `add_element` with no `element` field.
- Heading `level` outside 1–6.
- Table missing `rows`.
- Image missing `asset_id` AND `url`.

**Fix** — check `details.field` in the envelope. Cross-reference with the [Concepts → Spec format](/docs/scaiscribe/concepts) page or the JSON Schema. The SDKs help: the `PatchOp` model has typed shape that catches a lot at construct time.

## Ingestion warning: `INGEST_PANDOC_FOOTNOTE_DETECTED` / `INGEST_IMAGE_DROPPED` / fidelity score < 1.0

**Symptom** — `ingest_document` succeeds with `fidelity_score < 1.0` and warnings calling out specific elements.

**Cause** — the source document used a feature ScaiScribe's ingest path doesn't fully model yet. When pandoc is installed on the worker host it runs alongside mammoth to surface higher-fidelity warnings (footnotes, math equations, citations, definition lists, line blocks); without pandoc only the mammoth-detected gaps are reported.

**Fix** — this isn't a bug, it's an honest report. The body element survives — usually as a `paragraph` carrying the visible text, with the structural metadata flagged in `fidelity.warnings[]`. For ScaiScribe-authored documents the JSON breadcrumb at `scaiscribe/breadcrumbs.json` in the .docx zip provides full round-trip equivalence; externally-authored docs degrade gracefully with documented warnings.

## "Sync wrap timed out" / `JobTimeoutError`

**Symptom** — `finalize` or `ingest_document` raises a timeout exception after the per-operation budget.

**Cause** — the underlying job is taking longer than the SDK's sync-wrap window (8–30s depending on operation).

**Fix** — pass `sync=False`. You'll get a `JobEnvelope` back immediately; poll `get_job(job_id)` until `status` becomes `completed` or `failed`. This is the right pattern for any job you know will exceed the default sync timeout.

```python
job = scribe.ingest_document(big_docx, format="docx", sync=False)
while job.status not in ("completed", "failed"):
    time.sleep(2)
    job = scribe.get_job(job.job_id)
```

## Where to look when stuck

- **Always include the `request_id`** when reporting issues. It's in every error envelope and on every access log line on the backend.
- **`/v1/health`** is public — if it doesn't work, your network can't reach the backend at all.
- **`/v1/admin/config`** is public — if `auth_configured: false`, the backend isn't ready for ScaiKey auth.

For anything else, ping the platform team with the `request_id` of the failing call.
