odysseus-dev/odysseus · error · HTTPException
str(e)
Error message
str(e)
What it means
Bare 500 wrapper on PATCH /api/document/{doc_id}: any non-HTTP exception during metadata update (title/language change, session re-link via _get_session_or_404 internals, commit) becomes HTTPException(500, str(e)). Unlike sibling handlers it does not prefix the message, so the response body is just the raw exception text — useful for diagnosis but prone to leaking internals.
Source
Thrown at routes/document/document_routes.py:732
_get_session_or_404(db, req.session_id, user)
doc.session_id = req.session_id if req.session_id else None
if not req.session_id:
# Tab closed / doc detached from its session — drop the
# in-memory active-doc pointer so the last-resort injection
# path doesn't re-surface this doc in a later chat (#1160).
try:
from src.agent_tools.document_tools import clear_active_document
clear_active_document(doc_id)
except Exception as e:
logger.warning("Failed to clear active document %r on detach", doc_id, exc_info=e)
db.commit()
db.refresh(doc)
return _doc_to_dict(doc)
except HTTPException:
raise
except Exception as e:
db.rollback()
raise HTTPException(500, str(e))
finally:
db.close()
# ---- DELETE /api/document/{doc_id} — soft delete ----
@router.delete("/api/document/{doc_id}")
async def delete_document(request: Request, doc_id: str) -> Dict[str, str]:
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.is_active = False
# Closed/deleted — drop the in-memory active-doc pointer so it isn't
# re-injected into a later, unrelated chat (#1160).
try:
from src.agent_tools.document_tools import clear_active_documentView on GitHub (pinned to f9235ebbf1)
Solutions
- Read the response body — it contains the raw exception message from the failing stage.
- Add length/format validation on title/language/session_id in the DocumentPatch model so bad values 422 instead of 500.
- For lock errors, avoid concurrent PATCHes to the same doc or switch DB.
- Consider prefixing the message like the other handlers and confirming no sensitive internals leak to clients.
Example fix
# before
class DocumentPatch(BaseModel):
title: Optional[str] = None
language: Optional[str] = None # any string -> DB error -> 500 str(e)
# after
class DocumentPatch(BaseModel):
title: Optional[constr(max_length=200)] = None
language: Optional[Literal['markdown','python','pdf','email','text']] = None Defensive patterns
Strategy: validation
Validate before calling
const okTitle = (t) => typeof t === 'string' && t.length <= 200;
const okLang = (l) => ['markdown','python','pdf','email','text'].includes(l);
if (patch.title && !okTitle(patch.title)) throw new Error('bad title');
if (patch.language && !okLang(patch.language)) throw new Error('bad language');
await api.patch(`/api/document/${id}`, patch); Type guard
const LANGS = new Set(['markdown','python','pdf','email','text']);
function isSupportedLanguage(v: unknown): v is string {
return typeof v === 'string' && LANGS.has(v);
} Try / catch
try { await api.patch(`/api/document/${id}`, patch); }
catch (e) {
if (e.status === 500) { notify('Update failed: ' + e.body?.detail); return false; } // body carries raw cause
throw e;
} Prevention
- Constrain title length and language enum in the request model so bad input 422s instead of 500ing
- Validate PATCH payloads client-side
- Note that this handler leaks raw exception text — parse it for diagnosis but never display it verbatim to end users
When it happens
Trigger: Commit failure (lock, constraint on language/session_id length); invalid session_id taking an unexpected code path; None values slipping through the Pydantic DocumentPatch model into DB columns with constraints.
Common situations: Long language strings or invalid enum values accepted by the model but rejected by the DB; concurrent PATCHes on SQLite; session linkage pointing at a deleted session in a way that raises instead of 404ing.
Related errors
- Failed to delete calendar
- Failed to list calendars
- Failed to list events
- Failed to create event
- Failed to update event
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/e2cf673c7ead8401.
Report an issue: GitHub.