{"record":{"id":"410548bcad8df22f","repo":"lfnovo/open-notebook","slug":"no-class-found-for-table-table-name","errorCode":null,"errorMessage":"No class found for table {table_name}","messagePattern":"No class found for table (.+?)","errorType":"validation","errorClass":"InvalidInputError","httpStatus":400,"severity":"error","filePath":"open_notebook/domain/base.py","lineNumber":119,"sourceCode":"            logger.exception(e)\n            raise DatabaseOperationError(e)\n\n    @classmethod\n    async def get(cls: Type[T], id: str) -> T:\n        if not id:\n            raise InvalidInputError(\"ID cannot be empty\")\n        try:\n            # Get the table name from the ID (everything before the first colon)\n            table_name = id.split(\":\")[0] if \":\" in id else id\n\n            # If we're calling from a specific subclass and IDs match, use that class\n            if cls.table_name and cls.table_name == table_name:\n                target_class: Type[T] = cls\n            else:\n                # Otherwise, find the appropriate subclass based on table_name\n                found_class = cls._get_class_by_table_name(table_name)\n                if not found_class:\n                    raise InvalidInputError(f\"No class found for table {table_name}\")\n                target_class = cast(Type[T], found_class)\n\n            result = await repo_query(\"SELECT * FROM $id\", {\"id\": ensure_record_id(id)})\n            if result:\n                return target_class(**result[0])\n            else:\n                raise NotFoundError(f\"{table_name} with id {id} not found\")\n        except Exception as e:\n            logger.error(f\"Error fetching object with id {id}: {str(e)}\")\n            logger.exception(e)\n            raise NotFoundError(f\"Object with id {id} not found - {str(e)}\")\n\n    @classmethod\n    def _get_class_by_table_name(cls, table_name: str) -> Optional[Type[\"ObjectModel\"]]:\n        \"\"\"Find the appropriate subclass based on table_name.\"\"\"\n\n        def get_all_subclasses(c: Type[\"ObjectModel\"]) -> List[Type[\"ObjectModel\"]]:\n            all_subclasses: List[Type[\"ObjectModel\"]] = []","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/lfnovo/open-notebook/blob/a7de90d38aaf18ee85fd661854d35c11e44613e2/open_notebook/domain/base.py#L101-L137","documentation":"When get(id) is called and the table name embedded in the record id (the part before the first ':') does not match the calling class, the method searches all registered ObjectModel subclasses via _get_class_by_table_name. If no subclass declares that table_name, there is no model to deserialize the row into, so it raises InvalidInputError.","triggerScenarios":"await ObjectModel.get('sometable:abc123') where no model subclass has table_name='sometable'; typo'd table prefix in an id string; a model class that exists but was never imported (so not in the subclass registry).","commonSituations":"Ids constructed by string concatenation with a wrong table prefix; model module not imported anywhere so __subclasses__ never includes it; renamed table_name in a model while old ids still circulate in stored data or queues.","solutions":["Check the id's table prefix matches a real model (everything before the first ':')","Ensure the model module is imported so the subclass is discoverable","If the table was renamed, update the stored ids or add a compatibility mapping","Prefer calling get() on the specific class whose table matches the id"],"exampleFix":"# before\nobj = await ObjectModel.get(f\"{table}:{rid}\")  # table may be unknown\n\n# after\nfrom open_notebook.domain.model import Note\nobj = await Note.get(f\"note:{rid}\")","handlingStrategy":"type-guard","validationCode":"table = rid.split(\":\", 1)[0]\nif table not in KNOWN_TABLES:  # {'note', 'source', ...}\n    raise HTTPException(status_code=400, detail=f\"Unknown table '{table}'\")","typeGuard":"KNOWN = {c.table_name for c in ObjectModel.__subclasses__()}  # after imports\n\ndef table_is_registered(rid: str) -> bool:\n    return rid.split(\":\", 1)[0] in {c.table_name for c in all_subclasses(ObjectModel)}","tryCatchPattern":"from open_notebook.domain.errors import InvalidInputError\ntry:\n    obj = await ObjectModel.get(rid)\nexcept InvalidInputError as e:\n    raise HTTPException(status_code=400, detail=str(e))","preventionTips":["Construct record ids from the model class's table_name, not string literals","Keep a single registry/mapping of table names to model classes","Import all model modules at startup so subclass discovery works"],"tags":["open-notebook","record-id","model-registry","surrealdb"],"backgroundTag":"unknown-table-or-model-mapping","analyzedSha":"a7de90d38aaf18ee85fd661854d35c11e44613e2","analyzedAt":"2026-08-27T02:39:58.166Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}