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
- Verify both the creator helper and this route resolve to the same database URL/engine (log the bind of each session).
- With SQLite file DBs, check for a stale .db file in the working directory shadowing the expected one.
- Confirm the creator helper commits before returning the id.
- 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
- Bind all sessions to one engine in tests and production
- Never mix in-memory SQLite with file-based sessions in the same process
- Have creator helpers commit before returning ids
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
- Assistant session could not be resolved
- Failed to delete calendar
- Failed to list calendars
- Failed to create calendar
- Failed to update calendar
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/fb24a6403d5a69e7.
Report an issue: GitHub.