{"record":{"id":"26738d876d5a6d95","repo":"MemPalace/mempalace","slug":"db-path","errorCode":null,"errorMessage":"{db_path}","messagePattern":"\\{db_path\\}","errorType":"exception","errorClass":"PalaceNotFoundError","httpStatus":null,"severity":"error","filePath":"mempalace/backends/sqlite_exact.py","lineNumber":953,"sourceCode":"                return\n            handle.closed = True\n            try:\n                handle.conn.close()\n            except Exception:\n                logger.debug(\n                    \"Failed to close stale immutable sqlite_exact reader for %s\",\n                    palace_path,\n                    exc_info=True,\n                )\n\n    def _connect(self, palace_path: str, create: bool, *, read_only: bool = False):\n        if self._closed:\n            raise BackendClosedError(\"SQLiteExactBackend has been closed\")\n        if create and read_only:\n            raise ValueError(\"sqlite_exact read-only connections cannot create a palace\")\n        db_path = self._db_path(palace_path)\n        if not create and not os.path.isfile(db_path):\n            raise PalaceNotFoundError(db_path)\n        if create:\n            os.makedirs(palace_path, exist_ok=True)\n            try:\n                os.chmod(palace_path, 0o700)\n            except (OSError, NotImplementedError):\n                pass\n        # Hold the registry lock across cache-check + connect + schema init:\n        # two threads first-opening the same palace must not each create a\n        # connection (the loser leaked unclosed and outlived close()) nor run\n        # _init_schema concurrently on a fresh file, which surfaces transient\n        # \"database is locked\" errors before WAL mode is established. Only\n        # first-open pays for the I/O under the lock; cache hits are a dict\n        # probe.\n        with self._clients_lock:\n            if self._closed:\n                raise BackendClosedError(\"SQLiteExactBackend has been closed\")\n            clients = self._read_only_clients if read_only else self._clients\n            cached = clients.get(palace_path)","sourceCodeStart":935,"sourceCodeEnd":971,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/backends/sqlite_exact.py#L935-L971","documentation":"`PalaceNotFoundError` whose message is just the db file path, raised in `SQLiteExactBackend._connect`. When `create=False`, the backend verifies `palace/<name>.db` exists on disk before connecting; if not, it raises rather than letting sqlite silently create an empty database and mask the missing-palace bug.","triggerScenarios":"Calling any non-create API (get_collection with create=False, delete_collection, a read) against a palace path that has no db file — wrong path, typo in palace name, palace not yet built, or a fresh clone without the data directory.","commonSituations":"Wrong working directory so the relative palace path resolves elsewhere; first run on a new machine before `mempalace mine` created the palace; renaming/moving the palace directory; CI checkout without the data.","solutions":["Verify the path: `ls <palace_path>` and check the `*.db` file actually exists where you point the backend.","If the palace should be created, pass `create=True` (or run the mining command that builds it).","Use absolute palace paths (or resolve from config) instead of relative paths that shift with cwd.","Catch PalaceNotFoundError and surface an onboarding/setup hint to the user."],"exampleFix":"# before\nhandle = backend._connect(\"~/palaces/alice\", create=False)  # path typo / not built\n\n# after\npalace_path = str(Path.home() / \"palaces\" / \"alice\")\nif not Path(palace_path, \"mempalace.db\").is_file():\n    raise SystemExit(f\"palace not found at {palace_path}; run `mempalace mine` first\")\nhandle = backend._connect(palace_path, create=False)","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef palace_db_exists(backend, palace_path) -> bool:\n    return Path(backend._db_path(palace_path)).is_file()","typeGuard":null,"tryCatchPattern":"try:\n    col = backend.get_collection(palace, name)\nexcept PalaceNotFoundError as e:\n    raise SystemExit(f\"palace db missing at {e}; run `mempalace mine` first or fix the path\") from e","preventionTips":["Resolve palace paths to absolute paths from one config source at startup.","Run a build/mine step before any read path on fresh installs.","Catch PalaceNotFoundError to branch into first-run onboarding."],"tags":["sqlite-exact","palace-not-found","path","first-run"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}