odysseus-dev/odysseus · error · HTTPException
Note not found
Error message
Note not found
What it means
404 raised at routes/note/note_routes.py:695 in GET /notes/{note_id} when no Note row has that id. Distinct from the ownership 404 on the next line: this one means the id does not exist at all, for any user. The handler intentionally returns 404 (not 403) in both cases to avoid leaking which ids exist.
Source
Thrown at routes/note/note_routes.py:695
repeat=body.repeat or "none",
sort_order=body.sort_order if body.sort_order is not None else 0,
)
db.add(note)
db.commit()
db.refresh(note)
return _note_to_dict(note)
finally:
db.close()
# --- GET ONE ---
@router.get("/{note_id}")
def get_note(request: Request, note_id: str):
user = _owner(request)
db = SessionLocal()
try:
note = db.query(Note).filter(Note.id == note_id).first()
if not note:
raise HTTPException(404, "Note not found")
# SECURITY: strict ownership — previously `note.owner and note.owner != user`
# let any user touch a row whose owner field was null/empty.
if user is not None and note.owner != user:
raise HTTPException(404, "Note not found")
return _note_to_dict(note)
finally:
db.close()
# --- UPDATE ---
@router.put("/{note_id}")
def update_note(request: Request, note_id: str, body: NoteUpdate):
user = _owner(request)
db = SessionLocal()
try:
note = db.query(Note).filter(Note.id == note_id).first()
if not note:
raise HTTPException(404, "Note not found")
# SECURITY: strict ownership — previously `note.owner and note.owner != user`View on GitHub (pinned to f9235ebbf1)
Solutions
- Re-fetch the note list; if the id is gone, remove it from local state/URL.
- If it should exist, confirm you are pointed at the right environment/DB.
- Check the id is complete (not truncated by URL handling).
- Treat as terminal for that id — do not retry.
Example fix
// before
const note = await api.getNote(id); // throws on 404
// after
const res = await fetch(`/notes/${id}`);
if (res.status === 404) { router.replace('/notes'); return null; }
const note = await res.json(); Defensive patterns
Strategy: try-catch
Validate before calling
const ids = new Set((await api.listNotes()).map(n => n.id));
if (!ids.has(noteId)) { navigate('/notes'); } Try / catch
try { note = await api.getNote(noteId); }
catch (e) { if (e.status === 404) { router.replace('/notes'); return null; } throw e; } Prevention
- Do not bookmark individual note URLs as permanent references.
- Reconcile client caches with the server list after deletions.
When it happens
Trigger: Fetching a note deleted by the same user in another tab/device, a stale share/bookmark link after deletion, or a malformed/guessed note id. Also a note id from a different server/DB after a restore or environment switch.
Common situations: Bookmarked note URLs surviving deletion. Frontend list cache showing a note already removed. Split-brain between two app instances on different DBs.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/33aef865c46634bf.
Report an issue: GitHub.