lfnovo/open-notebook · error · HTTPException
Notebook not found
Error message
Notebook not found
What it means
HTTP 404 raised in save_insight_as_note when the domain layer raises NotFoundError — meaning the notebook_id supplied in the request body doesn't reference an existing notebook.
Source
Thrown at api/routers/insights.py:85
insight = await SourceInsight.get(insight_id)
if not insight:
raise HTTPException(status_code=404, detail="Insight not found")
# Use the existing save_as_note method from the domain model
note = await insight.save_as_note(request.notebook_id)
return NoteResponse(
id=note.id or "",
title=note.title,
content=note.content,
note_type=note.note_type,
created=str(note.created),
updated=str(note.updated),
)
except HTTPException:
raise
except NotFoundError:
raise HTTPException(status_code=404, detail="Notebook not found")
except InvalidInputError as e:
raise HTTPException(status_code=400, detail=str(e))
except OpenNotebookError:
raise
except Exception as e:
logger.error(f"Error saving insight {insight_id} as note: {str(e)}")
raise HTTPException(
status_code=500, detail="Error saving insight as note"
)
View on GitHub (pinned to a7de90d38a)
Solutions
- GET the notebooks list and use a current notebook_id
- If the intended notebook was deleted, create it first or choose another
- Ensure the client refreshes notebook ids after deletion events
Example fix
// before
{"notebook_id": "notebook:old456"}
// after
notebooks = await client.get("/api/notebooks").json()
{"notebook_id": notebooks[0]["id"]} Defensive patterns
Strategy: validation
Validate before calling
notebooks = (await client.get("/api/notebooks")).json()
if payload["notebook_id"] not in {n["id"] for n in notebooks}:
payload["notebook_id"] = notebooks[0]["id"] # or prompt Type guard
def is_notebook_id(nid: str, notebooks: list[dict]) -> bool:
return nid in {n["id"] for n in notebooks} Try / catch
try:
await client.post(f"/api/insights/{insight_id}/save-as-note", json=payload)
except httpx.HTTPStatusError as e:
if e.response.status_code == 404 and "Notebook" in e.response.text:
notebooks = (await client.get("/api/notebooks")).json()
payload["notebook_id"] = notebooks[0]["id"]
await client.post(f"/api/insights/{insight_id}/save-as-note", json=payload)
raise Prevention
- Fetch fresh notebook ids before save-as-note actions
- Don't cache default notebook ids across sessions
- When deleting a notebook, update clients/settings that reference it
When it happens
Trigger: POST /api/insights/{id}/save-as-note with a notebook_id that was deleted, never existed, or belongs to another environment.
Common situations: Default notebook deleted; frontend sending a cached/stale notebook id; multi-environment configs where notebook ids differ.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Insight not found
- Session not found
- Credential not found
- {embed_request.item_type} not found
- Rebuild command not found
AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27).
Data as JSON: /api/errors/e1b08323e16ee17b.
Report an issue: GitHub.