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.
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.
Appendix: 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)