{"record":{"id":"6307a43312ecd16b","repo":"lfnovo/open-notebook","slug":"id-cannot-be-empty","errorCode":null,"errorMessage":"ID cannot be empty","messagePattern":"ID cannot be empty","errorType":"validation","errorClass":"InvalidInputError","httpStatus":400,"severity":"error","filePath":"open_notebook/domain/base.py","lineNumber":107,"sourceCode":"\n            result = await repo_query(query)\n            objects = []\n            for obj in result:\n                try:\n                    objects.append(target_class(**obj))\n                except Exception as e:\n                    logger.critical(f\"Error creating object: {str(e)}\")\n\n            return objects\n        except Exception as e:\n            logger.error(f\"Error fetching all {cls.table_name}: {str(e)}\")\n            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:","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/lfnovo/open-notebook/blob/a7de90d38aaf18ee85fd661854d35c11e44613e2/open_notebook/domain/base.py#L89-L125","documentation":"ObjectModel.get(id) rejects empty/falsy IDs up front with InvalidInputError before touching the database. An empty string, None, or otherwise falsy id can never name a record, so the method fails fast.","triggerScenarios":"await MyModel.get(''), await MyModel.get(None), or a value pulled from an unvalidated request path/query param like /notes/ that arrives as empty string.","commonSituations":"API route handlers passing through unvalidated path parameters; frontend sending undefined which serializes to ''; default arguments like id='' used as placeholders.","solutions":["Validate/require the id at the API boundary (Path(..., min_length=1) in FastAPI)","Default to a 404 or 400 response when the id is missing instead of calling get()","Check truthiness before calling: if not id: raise HTTPException(404)"],"exampleFix":"# before\nobj = await Note.get(note_id)  # note_id may be ''\n\n# after\nif not note_id:\n    raise HTTPException(status_code=404, detail=\"Note id required\")\nobj = await Note.get(note_id)","handlingStrategy":"validation","validationCode":"if not id or not isinstance(id, str):\n    raise HTTPException(status_code=404, detail=\"Missing id\")\nobj = await Note.get(id)","typeGuard":"def is_valid_record_id(id: object) -> bool:\n    return isinstance(id, str) and bool(id.strip()) and \":\" in id","tryCatchPattern":null,"preventionTips":["Require non-empty path params at the FastAPI layer (min_length=1)","Never default id parameters to '' or None when a fetch follows","Return early with 400/404 when id is missing instead of calling the ORM"],"tags":["validation","empty-id","open-notebook","api-boundary"],"backgroundTag":"missing-required-parameter","analyzedSha":"a7de90d38aaf18ee85fd661854d35c11e44613e2","analyzedAt":"2026-08-27T02:39:58.166Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}