lfnovo/open-notebook · error · NotFoundError

Object with id {id} not found - {str(e)}

Error message

Object with id {id} not found - {str(e)}

What it means

The broad except in get() converts any unexpected exception during fetch/deserialization into NotFoundError with the original message appended. Unfortunately this masks genuine infrastructure errors (connection loss, schema mismatch) as 'not found', because the except wraps the whole body including the success path's constructor call.

Source

Thrown at open_notebook/domain/base.py:130

            # 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:
                return subclass
        return None

    async def save(self) -> None:

View on GitHub (pinned to a7de90d38a)

Solutions

  1. Read the full message suffix and the logged traceback to find the real cause
  2. If it's connection-related, restore SurrealDB and retry (make database / check API logs)
  3. If it's deserialization, fix schema drift between the Pydantic model and stored records
  4. Treat ambiguous 'not found' during outages as errors, not 404s — check system health before returning 404 to users

Example fix

# before
except NotFoundError:
    return JSONResponse(status_code=404)  # may mask a DB outage

# after
except NotFoundError as e:
    if "connection" in str(e).lower():
        return JSONResponse(status_code=503, content={"detail": "database unavailable"})
    return JSONResponse(status_code=404, content={"detail": "not found"})
Defensive patterns

Strategy: fallback

Validate before calling

null

Try / catch

from open_notebook.domain.errors import NotFoundError
try:
    obj = await Note.get(nid)
except NotFoundError as e:
    msg = str(e).lower()
    if any(k in msg for k in ("connection", "timeout", "serialization")):
        raise HTTPException(status_code=503, detail="Storage error")
    raise HTTPException(status_code=404, detail="Not found")

Prevention

When it happens

Trigger: DB connection drops mid-query, ensure_record_id raises on a malformed id, or target_class(**result[0]) fails on schema drift — all surface as NotFoundError('Object with id ... not found - <cause>').

Common situations: Developer sees 'not found' but the record clearly exists; usually a SurrealDB outage or model/record field mismatch hiding in the - {str(e)} suffix. Read the log (logger.exception prints the traceback) to identify the real cause.

Related errors


AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27). Data as JSON: /api/errors/69746c283d3a1754. Report an issue: GitHub.