> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ndi.nace.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Symptom-based fixes for auth, uploads, ingestion, jobs, and search

## Authentication

**401 / missing key.** Send `X-API-Key: ndi_sk_…`. `Authorization: Bearer` works
on hosted MCP only. Check `$NDI_API_KEY` is exported in the same shell as the
client.

**Wrong host.** Default base is `https://ndi-api.nace.ai`. Local stacks need
`$NDI_BASE_URL`. Paths are `/v1/...`, not `/api/v1/...`.

See [Authentication](/authentication).

## Invalid requests

**422 `invalid_request`.** Extra JSON fields on a strict body — especially
`wait_seconds` or `Idempotency-Key` inside JSON. Those are a query parameter
and a header.

**Classify with `parse_result`.** Classify reads the original file. Upload or
`workspace_file` only.

**`qa_file` on Excel.** Use [query tables](/guides/query-tables).

**Access labels as strings.** Workspace `access.labels` is
`[{ "name", "description" }, ...]`. Names cannot contain hyphens.

## Uploads

**`upload_expired`.** Granular `POST /v1/uploads` handles are single-use and
default to 24 hours (7-day cap). Create a new upload.

**`file_too_large`.** Use a [workspace upload session](/guides/large-uploads)
(SDK auto-chunks at 32 MiB) or tighten `max_bytes` on a grant.

**`path_conflict`.** A workspace path is taken. Pass `on_conflict=new_version`
or pick another path.

**Browser 401 on grant redeem.** Send `X-Upload-Token`, not your API key.

## Ingestion and coverage

**Search or QA returns nothing.** Upload does not ingest. Call
[ingest](/guides/ingest-reconcile), then retry. `file_not_ingested` is 409.

**`coverage.restricted_candidates` > 0.** Your key is missing labels listed in
`labels_required`. See [Access labels](/guides/access-labels).

**Stale results after replace.** Ingest with `stale_only=True` or run
`reconcile`.

## Jobs

**Stuck in `queued` / `running`.** Poll `GET /v1/jobs/{id}` or stream
`/events`. Cancel with `POST /v1/jobs/{id}/cancel`. `job_not_cancellable` means
it already finished.

**`result_expired`.** The job row remains; retention dropped the payload.
Re-run the operation. `jobs.request` expires with the same TTL.

**zsh poll loop parse error.** Compare `STATUS` with `==`, not
`=~ succeeded|failed|cancelled`.

## Rate and concurrency

**429.** Back off using `Retry-After` if present. SDK clients retry idempotent
reads and replay-safe writes that carry an `Idempotency-Key`.

**Slow batch.** Share one `AsyncNdiClient` / `NdiClient` across tasks. Opening
a client per file exhausts the connection pool.

## Search sessions

**Deep search follow-up ignores context.** Pass that turn's `session_id`. Do
not reuse one session across a suite of unrelated questions.

## SDK parity

TypeScript does not wrap `search.filtered`, `tools.run_sql`, `jobs.request`,
or `jobs.delete`. Call those HTTP routes. Extraction schema CRUD is REST-only
in both languages.

See [Errors and limits](/concepts/errors).
