odysseus-dev/odysseus · warning · HTTPException
Document not found
Error message
Document not found
What it means
404 from GET /api/document/{doc_id}: no Document row matches the given id before ownership checks run. The id may be malformed, the document may have been soft-deleted by a path that filters it from this query, or it simply never existed.
Source
Thrown at routes/document/document_routes.py:476
q = db.query(Document).filter(
Document.session_id == session_id
)
if user:
q = q.filter(or_(Document.owner == user, Document.owner.is_(None)))
docs = q.order_by(Document.created_at.desc()).all()
return [_doc_to_dict(d) for d in docs]
finally:
db.close()
# ---- GET /api/document/{doc_id} ----
@router.get("/api/document/{doc_id}")
async def get_document(request: Request, doc_id: str) -> Dict[str, Any]:
user = get_current_user(request)
db = SessionLocal()
try:
doc = db.query(Document).filter(Document.id == doc_id).first()
if not doc:
raise HTTPException(404, "Document not found")
_verify_doc_owner(db, doc, user)
return _doc_to_dict(doc)
finally:
db.close()
# ---- POST /api/document/{doc_id}/archive — soft-archive / restore ----
@router.post("/api/document/{doc_id}/archive")
async def archive_document(request: Request, doc_id: str, archived: bool = Query(True)) -> Dict[str, Any]:
user = get_current_user(request)
db = SessionLocal()
try:
doc = db.query(Document).filter(Document.id == doc_id).first()
if not doc:
raise HTTPException(404, "Document not found")
_verify_doc_owner(db, doc, user)
doc.archived = bool(archived)
db.commit()
return {"ok": True, "id": doc_id, "archived": doc.archived}View on GitHub (pinned to f9235ebbf1)
Solutions
- Verify the doc id against GET /api/documents/library to confirm it still exists for this user.
- If it was soft-deleted, restore it through the app's restore path instead of retrying the GET.
- Fix the client to clear doc references when a delete/404 is returned rather than re-requesting the dead id.
- Check the URL for encoding issues (truncated ids, stray characters).
Example fix
// before
const doc = await api.get(`/api/document/${id}`); // throws on 404
// after
const doc = await api.get(`/api/document/${id}`).catch((e) => {
if (e.status === 404) { router.push('/library'); return null; }
throw e;
}); Defensive patterns
Strategy: try-catch
Validate before calling
const exists = (await api.get('/api/documents/library')).documents.some(d => d.id === id);
if (!exists) { router.push('/library'); return; } Type guard
function isDocId(id: unknown): id is string {
return typeof id === 'string' && /^[A-Za-z0-9_-]{6,}$/.test(id);
} Try / catch
try { return await api.get(`/api/document/${id}`); }
catch (e) { if (e.status === 404) { dropDocReference(id); return null; } throw e; } Prevention
- Purge doc references from client state on delete/404
- Validate id shape before building URLs
- Never auto-retry 404s — they are permanent for that id
When it happens
Trigger: Client holds a stale doc id from an earlier session (document deleted via DELETE /api/document/{doc_id}); typo'd or truncated UUID in the URL; the doc exists but with an archived flag handled by a different listing route.
Common situations: Browser refresh on a doc editor after the doc was deleted elsewhere; deep links bookmarked to deleted docs; frontend state desync after bulk operations.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/9bd5a2c591b0472e.
Report an issue: GitHub.