{"record":{"id":"8ecaf4ee58f53594","repo":"musistudio/claude-code-router","slug":"backend-ownerid-sqlite-store-close-failed","errorCode":null,"errorMessage":"[backend:${ownerId}] SQLite store close failed: ${formatError(error)}","messagePattern":"\\[backend:(.+?)\\] SQLite store close failed: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/core/src/plugins/backend-service.ts","lineNumber":124,"sourceCode":"    if (options.migrate) {\n      await options.migrate(database);\n      store.persist();\n    }\n    return store;\n  }\n\n  async stopOwner(ownerId: string): Promise<void> {\n    const backends = this.backends.filter((backend) => backend.ownerId === ownerId);\n    this.backends = this.backends.filter((backend) => backend.ownerId !== ownerId);\n    await Promise.all(backends.map((backend) => closeServer(backend.server)));\n\n    const sqliteStores = this.sqliteStores.filter((store) => store.ownerId === ownerId);\n    this.sqliteStores = this.sqliteStores.filter((store) => store.ownerId !== ownerId);\n    for (const store of sqliteStores) {\n      try {\n        store.close();\n      } catch (error) {\n        console.warn(`[backend:${ownerId}] SQLite store close failed: ${formatError(error)}`);\n      }\n    }\n  }\n\n  async stopAll(): Promise<void> {\n    const ownerIds = new Set([\n      ...this.backends.map((backend) => backend.ownerId),\n      ...this.sqliteStores.map((store) => store.ownerId)\n    ]);\n    await Promise.all([...ownerIds].map((ownerId) => this.stopOwner(ownerId)));\n  }\n}\n\nclass SqliteStoreImpl implements SqliteStore {\n  constructor(\n    readonly ownerId: string,\n    readonly dbFile: string,\n    readonly database: SqlDatabase","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/plugins/backend-service.ts#L106-L142","documentation":"Emitted by BackendService.stopOwner when closing one of an owner's SQLite stores throws. Each store owned by the plugin/owner being stopped is closed in a loop; a throw from store.close() (e.g. an unclosed prepared statement, busy database, or I/O error during checkpoint) is caught per-store, logged, and shutdown continues for the remaining stores. Shutdown never aborts because of it.","triggerScenarios":"Calling stopAll, stop, rollbackConfiguredPluginLoad, or stopBrowserWebSearchMcpServers while a SQLite database file is locked, on a read-only/full disk, or when the store has open cursors/statements that make close() throw.","commonSituations":"Disk full or permissions changed under the state directory; another process (or an in-flight query) holds the SQLite file; plugin rollback racing with active writes during a config reload.","solutions":["Free disk space and verify write permissions on the SQLite state directory.","Ensure no other process opens the same SQLite files (single-owner access).","If it happens on every shutdown after a crash, stop everything, delete/recreate the affected store file (data loss limited to that store), and restart.","Upgrade the core package — newer SQLite drivers checkpoint more gracefully on close."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Ensure single-owner access to SQLite state files","Free disk space before shutdown-heavy operations","Treat per-store close warnings as non-fatal but investigate repeats"],"tags":["sqlite","shutdown","resource-cleanup","plugins"],"backgroundTag":"sqlite-close-failed","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}