odysseus-dev/odysseus · error · HTTPException

Created document not found

Error message

Created document not found

What it means

500 raised by POST /api/documents/import-pdf when the creator returned a doc_id but the immediate follow-up query db.query(Document).filter(Document.id == doc_id).first() finds no row. The document either was never committed, was rolled back, or has a different id than reported.

Source

Thrown at routes/document/document_routes.py:310

                title=title,
                intro_text=body_text,
            )
        else:
            doc_id = create_plain_pdf_document(
                session_id=session_id,
                upload_id=upload_id,
                title=title,
                body_text=body_text,
            )

        if not doc_id:
            raise HTTPException(500, "Failed to create document for PDF")

        db = SessionLocal()
        try:
            doc = db.query(Document).filter(Document.id == doc_id).first()
            if not doc:
                raise HTTPException(500, "Created document not found")
            # The PDF doc creators stamp owner from the session only; a
            # session-less library import leaves owner NULL, which the Library's
            # owner filter then hides. Stamp the requesting user so it shows.
            if not doc.owner and user:
                doc.owner = user
                db.commit()
                db.refresh(doc)
            return _doc_to_dict(doc)
        finally:
            db.close()

    # ---- GET /api/documents/library ----
    @router.get("/api/documents/library")
    async def documents_library(
        request: Request,
        search: Optional[str] = Query(None),
        language: Optional[str] = Query(None),
        sort: str = Query("recent"),

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Verify both the creator helper and this route resolve to the same database URL/engine (log the bind of each session).
  2. With SQLite file DBs, check for a stale .db file in the working directory shadowing the expected one.
  3. Confirm the creator helper commits before returning the id.
  4. In tests, use a shared engine/fixture instead of per-call in-memory sessions.

Example fix

# before
# helper: s = SessionLocal(); s.add(doc); s.flush(); s.close()  # no commit -> row lost
# after
s = SessionLocal()
s.add(doc)
s.commit()
return doc.id
Defensive patterns

Strategy: validation

Validate before calling

def same_engine(a: Session, b: Session) -> bool:
    return a.bind is b.bind or str(a.bind.url) == str(b.bind.url)
assert same_engine(creator_session, SessionLocal()), 'creator and route must share one DB'

Try / catch

try { await api.post('/api/documents/import-pdf', fd); }
catch (e) { if (/Created document not found/.test(e.message)) { /* env/config bug: report, don't retry */ throw new ConfigError(e.message); } throw e; }

Prevention

When it happens

Trigger: The create_* helper commits on a different session/engine (e.g. its own SessionLocal bound to another DB file or schema) than the SessionLocal used for the lookup; the helper flushed but never committed and its session closed, discarding the row; a race where the row is deleted between create and read.

Common situations: Tests running with an in-memory SQLite DB where each SessionLocal gets a fresh database; multiple database URLs configured (dev vs prod config) so the write and read hit different databases; refactor changed the helper to use a context-managed session that rolls back on exit.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/fb24a6403d5a69e7. Report an issue: GitHub.