{"record":{"id":"11ac0a8ab78ed784","repo":"zylon-ai/private-gpt","slug":"failed-to-inspect-the-database-schema-11ac0a","errorCode":null,"errorMessage":"Failed to inspect the database schema.","messagePattern":"Failed to inspect the database schema\\.","errorType":"exception","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"private_gpt/components/database/table_like_inspector.py","lineNumber":33,"sourceCode":"\n\nclass DatabaseTableLikeInspector(DatabaseObjectInspector, ABC):\n    @abstractmethod\n    def get_objects(self, schema: str) -> list[InspectedDatabaseObject]:\n        pass\n\n    @abstractmethod\n    def get_inspector_type(self) -> str:\n        pass\n\n    def _extract_schema(\n        self, schema: str, table_name: str, obj_class: type[InspectedTableLike]\n    ) -> InspectedTableLike:\n        self._ensure_connected()\n        meta = inspect(self._engine)\n\n        if not meta:\n            raise ValueError(\"Failed to inspect the database schema.\")\n\n        table_key = (schema, table_name)\n\n        # Get columns\n        multi_cols = meta.get_multi_columns(\n            schema=schema, filter_names=[table_name], kind=obj_class.get_kind()\n        )\n        cols = multi_cols.get(table_key, [])\n\n        # Get primary key\n        multi_pks = meta.get_multi_pk_constraint(\n            schema=schema, filter_names=[table_name], kind=obj_class.get_kind()\n        )\n        pk = multi_pks.get(table_key, {})\n\n        # Get foreign keys\n        multi_fks = meta.get_multi_foreign_keys(\n            schema=schema, filter_names=[table_name], kind=obj_class.get_kind()","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/database/table_like_inspector.py#L15-L51","documentation":"ValueError raised inside DatabaseTableLikeInspector._extract_schema, the shared helper used by both the table and view inspectors. After _ensure_connected(), it calls inspect(self._engine) and treats a falsy result as a total inspection failure before reading columns, PK/FK constraints for the (schema, table_name) pair. Because this sits under both subclasses, one bad engine breaks every per-object extraction.","triggerScenarios":"Any call that reaches _extract_schema — i.e. get_objects() on DatabaseTableInspector or DatabaseViewInspector, or direct extraction for a single table/view — while inspect(self._engine) returns falsy, typically because the engine is broken after the connection check passed.","commonSituations":"Connection dropped between _ensure_connected() and the inspect() call (transient network blip, idle timeout); driver incompatibility after a SQLAlchemy upgrade; mocked engines in unit tests; permissions revoked on the information schema mid-session.","solutions":["Confirm the database is reachable and the engine can run a trivial query right before inspection","Retry once — transient drops between connect and inspect are the most common cause","Check SQLAlchemy/driver version compatibility (inspect() behavior changed across major versions)","In tests, patch inspect to return a real sqlalchemy.Inspector instead of None"],"exampleFix":"# before\ndef test_columns(engine):\n    monkeypatch.setattr('sqlalchemy.inspect', lambda e: None)  # trips the guard\n\n# after\nfrom sqlalchemy import inspect as sa_inspect\nmonkeypatch.setattr('sqlalchemy.inspect', sa_inspect)","handlingStrategy":"try-catch","validationCode":"from sqlalchemy import inspect as sa_inspect, text\n\nwith engine.connect() as c:\n    c.execute(text('SELECT 1'))\nmeta = sa_inspect(engine)\nassert meta is not None  # mirrors the library's own guard","typeGuard":null,"tryCatchPattern":"try:\n    obj = inspector._extract_schema(schema, table_name, InspectedTable)\nexcept ValueError as e:\n    if 'Failed to inspect the database schema' in str(e):\n        logger.warning('schema extraction failed for %s.%s; reconnecting', schema, table_name)\n        engine.dispose()\n        obj = inspector._extract_schema(schema, table_name, InspectedTable)\n    else:\n        raise","preventionTips":["Enable pool_pre_ping and reasonable pool_recycle on the engine","Retry once on this generic guard — most causes are transient drops","In tests, fake inspect() with a real sqlalchemy.Inspector or a non-None stub"],"tags":["database","sqlalchemy","schema-introspection","connection"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}