Skip to content

fix(documents): robust ingestion errors, status endpoint, and re-ingest - #283

Merged
arantespp merged 3 commits into
mainfrom
claude/document-ingestion-errors-hamku8
Jun 26, 2026
Merged

fix(documents): robust ingestion errors, status endpoint, and re-ingest#283
arantespp merged 3 commits into
mainfrom
claude/document-ingestion-errors-hamku8

Conversation

@arantespp

Copy link
Copy Markdown
Member

Summary

Fixes a cluster of document-ingestion pain points (issues #3#7):

# Problem Fix
3 Sync ingest of large files returned [object Object] and silently timed out failure_reason is now normalized (DomainError code → Error.message → string fallback, never [object Object]). Files larger than SYNC_INGESTION_MAX_BYTES (default 10 MB) are rejected up front with 413 FILE_TOO_LARGE_FOR_SYNC and guidance to use async mode.
4 Documents stuck in processing forever, no recovery Documents in pending/processing with no progress past INGESTION_STALL_TIMEOUT_MS (default 5 min) self-recover to failed with INGESTION_TIMEOUT when read.
5 & 6 get-document (6–8 MB) was the only way to check status New lightweight GET /documents/:id/status{ status, chunk_count, total_pages, error? }. Never returns chunk content.
7 No way to re-process without delete + re-upload New POST /documents/:id/ingest re-runs ingestion against the existing source file (recover a stuck doc or re-chunk with a different strategy). Async by default (202), ?async=false for 201.

Implementation notes

  • Business rules live in src/lib/documentIngestion.ts (sync-size guard, error normalization, stall recovery, re-ingest) and src/lib/documents.ts (getDocumentStatus). Route handlers stay thin.
  • Both new operations flow into the SDK, CLI route manifest, and MCP tool surface automatically from the OpenAPI spec (get-document-status, reingest-document MCP tools).
  • New error code FILE_TOO_LARGE_FOR_SYNC (413) added to the registry.
  • Two new optional env vars documented: SYNC_INGESTION_MAX_BYTES, INGESTION_STALL_TIMEOUT_MS.

Tests

Red/green TDD. Added unit coverage in documents.test.ts ([object Object] fix, 413 guard, status endpoint incl. self-recovery, re-ingest incl. async/stuck/404/401/403), MCP coverage in mcp.test.ts, and end-to-end steps in tests/smoke-tests.sh.

⚠️ The server unit tests run on testcontainers (PostgreSQL), which is unavailable in the web execution environment (no Docker), so they were validated via pnpm typecheck + pnpm eslint locally and rely on CI to run the full suite.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DPxCHtSDFGM7go7wDSVgsP


Generated by Claude Code

claude added 2 commits June 26, 2026 06:27
Addresses several document ingestion pain points:

- Synchronous ingestion of large files no longer surfaces "[object Object]".
  `failure_reason` is now normalized (DomainError code / Error message /
  string fallback), and files over SYNC_INGESTION_MAX_BYTES (default 10 MB)
  are rejected up front with 413 FILE_TOO_LARGE_FOR_SYNC and guidance to use
  async mode.
- Documents stuck in `pending`/`processing` self-recover: when read past
  INGESTION_STALL_TIMEOUT_MS (default 5 min) with no progress they transition
  to `failed` with `INGESTION_TIMEOUT`.
- New lightweight GET /documents/:id/status returning
  { status, chunk_count, total_pages, error? } for cheap polling instead of
  the full (multi-MB) get-document payload.
- New POST /documents/:id/ingest to re-process an existing document (recover a
  stuck doc or re-chunk with a different strategy) without delete + re-upload.

Both new operations flow into the SDK, CLI manifest, and MCP tool surface via
the OpenAPI spec. Adds unit + MCP + smoke coverage and updates module docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DPxCHtSDFGM7go7wDSVgsP
The status endpoint's fields were ambiguous during processing:
chunk_count came from metadata (only written on completion) with a DB-count
fallback, and total_pages was simply absent until ready.

- chunk_count is now always the live count of indexed chunks, so it is a
  meaningful progress signal during processing and equals the final total when
  ready (0 while pending).
- total_pages is explicitly null until extraction finishes (ready/failed),
  documented as distinct from "zero pages".

Updates the OpenAPI schema descriptions, module docs (field table), and adds a
test asserting total_pages is null for a non-paged plain-text document.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DPxCHtSDFGM7go7wDSVgsP
@github-actions

Copy link
Copy Markdown

Deploy Outputs

Package Stack Output Key Output Value
@soat/website SoatWebsite-claude-document-ingestion-errors-hamku8 BucketWebsiteURL http://soatwebsite-claude-document-ingestion-staticbucket-kek06mvwjyhe.s3-website-us-east-1.amazonaws.com

Chunks are now persisted incrementally as each embedding resolves (instead of
one batch at the end), and the pipeline records total_chunks/total_pages up
front and periodically refreshes indexed_chunks. This lets the status endpoint
report real progress while a document is still processing.

GET /documents/:id/status now returns:
- total_chunks — planned total (denominator), null until chunking starts
- progress — chunk_count / total_chunks as a percentage: 0 while pending,
  climbing while processing (capped at 99), 100 when ready, null when failed

The periodic metadata refresh also bumps updatedAt, preventing a long-running
ingestion from being misdetected as stalled.

Updates OpenAPI schema, module docs (field table + example), and tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DPxCHtSDFGM7go7wDSVgsP
@arantespp
arantespp enabled auto-merge (squash) June 26, 2026 06:39
@arantespp
arantespp merged commit e4cc102 into main Jun 26, 2026
4 checks passed
@arantespp
arantespp deleted the claude/document-ingestion-errors-hamku8 branch June 26, 2026 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants