lfnovo/open-notebook · warning · NotFoundError
{table_name} with id {id} not found
Error message
{table_name} with id {id} not found What it means
get(id) executed the query successfully but SurrealDB returned no row for the given record id, so there is nothing to deserialize and NotFoundError is raised. This is the normal 'record does not exist' signal, not an infrastructure failure.
Source
Thrown at open_notebook/domain/base.py:126
try:
# Get the table name from the ID (everything before the first colon)
table_name = id.split(":")[0] if ":" in id else id
# If we're calling from a specific subclass and IDs match, use that class
if cls.table_name and cls.table_name == table_name:
target_class: Type[T] = cls
else:
# Otherwise, find the appropriate subclass based on table_name
found_class = cls._get_class_by_table_name(table_name)
if not found_class:
raise InvalidInputError(f"No class found for table {table_name}")
target_class = cast(Type[T], found_class)
result = await repo_query("SELECT * FROM $id", {"id": ensure_record_id(id)})
if result:
return target_class(**result[0])
else:
raise NotFoundError(f"{table_name} with id {id} not found")
except Exception as e:
logger.error(f"Error fetching object with id {id}: {str(e)}")
logger.exception(e)
raise NotFoundError(f"Object with id {id} not found - {str(e)}")
@classmethod
def _get_class_by_table_name(cls, table_name: str) -> Optional[Type["ObjectModel"]]:
"""Find the appropriate subclass based on table_name."""
def get_all_subclasses(c: Type["ObjectModel"]) -> List[Type["ObjectModel"]]:
all_subclasses: List[Type["ObjectModel"]] = []
for subclass in c.__subclasses__():
all_subclasses.append(subclass)
all_subclasses.extend(get_all_subclasses(subclass))
return all_subclasses
for subclass in get_all_subclasses(ObjectModel):
if hasattr(subclass, "table_name") and subclass.table_name == table_name:View on GitHub (pinned to a7de90d38a)
Solutions
- Catch NotFoundError and surface a 404 to the user
- If the id came from a cached list, refresh the list / purge stale ids
- Verify the id by listing records in that table first when debugging
Example fix
# before
note = await Note.get(note_id) # unhandled NotFoundError
# after
from open_notebook.domain.errors import NotFoundError
try:
note = await Note.get(note_id)
except NotFoundError:
raise HTTPException(status_code=404, detail="Note not found") Defensive patterns
Strategy: try-catch
Try / catch
from open_notebook.domain.errors import NotFoundError
try:
note = await Note.get(note_id)
except NotFoundError:
raise HTTPException(status_code=404, detail="Note not found") Prevention
- Treat ids from caches/exports as potentially stale; re-validate before use
- Catch NotFoundError at the API boundary and map to 404 instead of 500
- Prefer listing fresh from the DB when building user-facing id lists
When it happens
Trigger: await Note.get('note:abc') where that record was deleted or never existed; race where another process deleted the row between listing and get; id copied with a wrong/typo'd key portion.
Common situations: Stale ids cached in the frontend or embedded in notebook cells/queues after the source was deleted; user follows an old link; concurrent cleanup jobs.
Related errors
- No class found for table {table_name}
- Object with id {id} not found - {str(e)}
- Failed to delete record: {str(e)}
- Error fetching all {cls.table_name}: {str(e)}
- Error saving record: {e}
AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27).
Data as JSON: /api/errors/4a1509465ff4f04b.
Report an issue: GitHub.