{"record":{"id":"33aef865c46634bf","repo":"odysseus-dev/odysseus","slug":"note-not-found","errorCode":null,"errorMessage":"Note not found","messagePattern":"Note not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"routes/note/note_routes.py","lineNumber":695,"sourceCode":"                repeat=body.repeat or \"none\",\n                sort_order=body.sort_order if body.sort_order is not None else 0,\n            )\n            db.add(note)\n            db.commit()\n            db.refresh(note)\n            return _note_to_dict(note)\n        finally:\n            db.close()\n\n    # --- GET ONE ---\n    @router.get(\"/{note_id}\")\n    def get_note(request: Request, note_id: str):\n        user = _owner(request)\n        db = SessionLocal()\n        try:\n            note = db.query(Note).filter(Note.id == note_id).first()\n            if not note:\n                raise HTTPException(404, \"Note not found\")\n            # SECURITY: strict ownership — previously `note.owner and note.owner != user`\n            # let any user touch a row whose owner field was null/empty.\n            if user is not None and note.owner != user:\n                raise HTTPException(404, \"Note not found\")\n            return _note_to_dict(note)\n        finally:\n            db.close()\n\n    # --- UPDATE ---\n    @router.put(\"/{note_id}\")\n    def update_note(request: Request, note_id: str, body: NoteUpdate):\n        user = _owner(request)\n        db = SessionLocal()\n        try:\n            note = db.query(Note).filter(Note.id == note_id).first()\n            if not note:\n                raise HTTPException(404, \"Note not found\")\n            # SECURITY: strict ownership — previously `note.owner and note.owner != user`","sourceCodeStart":677,"sourceCodeEnd":713,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/note/note_routes.py#L677-L713","documentation":"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.","triggerScenarios":"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.","commonSituations":"Bookmarked note URLs surviving deletion. Frontend list cache showing a note already removed. Split-brain between two app instances on different DBs.","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."],"exampleFix":"// before\nconst note = await api.getNote(id);  // throws on 404\n\n// after\nconst res = await fetch(`/notes/${id}`);\nif (res.status === 404) { router.replace('/notes'); return null; }\nconst note = await res.json();","handlingStrategy":"try-catch","validationCode":"const ids = new Set((await api.listNotes()).map(n => n.id));\nif (!ids.has(noteId)) { navigate('/notes'); }","typeGuard":null,"tryCatchPattern":"try { note = await api.getNote(noteId); }\ncatch (e) { if (e.status === 404) { router.replace('/notes'); return null; } throw e; }","preventionTips":["Do not bookmark individual note URLs as permanent references.","Reconcile client caches with the server list after deletions."],"tags":["fastapi","http-404","notes","crud"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}