{"record":{"id":"354059b8976f29f7","repo":"can1357/oh-my-pi","slug":"persistent-credential-block-store-store-is-unav","errorCode":null,"errorMessage":"Persistent credential block store ${store} is unavailable after SQLite corruption","messagePattern":"Persistent credential block store (.+?) is unavailable after SQLite corruption","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/ai/src/auth-storage.ts","lineNumber":1941,"sourceCode":"\t\t\t\tcredentialId,\n\t\t\t\tprovider,\n\t\t\t\tproviderKey,\n\t\t\t\tblockScope,\n\t\t\t\tblockedUntilMs: nextBlockedUntil,\n\t\t\t});\n\t\t}\n\t}\n\n\t#handlePersistedBlockStoreError(err: unknown): boolean {\n\t\tif (!isSqliteCorruptionError(err)) return false;\n\t\tthis.#reportDamagedBlockStore(err);\n\t\treturn true;\n\t}\n\n\t#assertPersistedBlockStoreWritable(): void {\n\t\tif (!this.#persistedBlockStoreDamaged) return;\n\t\tconst store = this.#sourceLabel ?? `local ${getAgentDbPath()}`;\n\t\tthrow new Error(`Persistent credential block store ${store} is unavailable after SQLite corruption`);\n\t}\n\n\t/**\n\t * Latches {@link AuthStorage.#persistedBlockStoreDamaged} on the first\n\t * unrecoverable persisted-block store error and surfaces it once at `error`\n\t * level with the store location, so an operator can repair or replace it.\n\t * Later reads/writes short-circuit silently — the in-memory backoff map keeps\n\t * rate-limit blocks applying for the life of the process; only cross-process\n\t * persistence is lost.\n\t */\n\t#reportDamagedBlockStore(err: unknown): void {\n\t\tif (this.#persistedBlockStoreDamaged) return;\n\t\tthis.#persistedBlockStoreDamaged = true;\n\t\tconst store = this.#sourceLabel ?? `local ${getAgentDbPath()}`;\n\t\tlogger.error(\n\t\t\t\"Persistent credential store is corrupt; cross-process rate-limit persistence is disabled for this process. In-memory backoff still applies. Repair the store with `sqlite3 <path> '.recover'` or delete it to recreate on next login.\",\n\t\t\t{ err, store },\n\t\t);","sourceCodeStart":1923,"sourceCodeEnd":1959,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/auth-storage.ts#L1923-L1959","documentation":"AuthStorage latches #persistedBlockStoreDamaged when the persisted credential block store (SQLite-backed) hits unrecoverable corruption. Once latched, #assertPersistedBlockStoreWritable throws this Error on any operation requiring the persisted store, with the store location so the operator can repair or replace it. The library refuses to write credentials into a known-corrupt store rather than risk data loss.","triggerScenarios":"Any AuthStorage operation that must read/write persisted credential blocks after an earlier SQLite error latched the damage flag (e.g. storing, listing, or blocking credentials), when the underlying SQLite database at getAgentDbPath() (or a remote #sourceLabel store) is corrupt.","commonSituations":"Disk full/crash mid-write corrupting the agent SQLite db; file copied while the app was running; incompatible/older SQLite file format after version downgrade; permission issues manifesting as persistent store errors.","solutions":["Back up and delete/repair the corrupted database at the path in the message (local default from getAgentDbPath()), then re-login to repopulate credentials","Run SQLite integrity repair (e.g. sqlite3 .recover / .dump to a new file) on the db before deleting it","Check disk space and filesystem health to prevent recurrence","If #sourceLabel names a remote store, repair or replace that store, then restart the process"],"exampleFix":"// before\nawait storage.storeCredential(credential); // throws: store damaged\n// after\n// mv ~/.omp/agent.db ~/.omp/agent.db.corrupt  (or run sqlite3 .recover)\n// restart process, re-run OAuth logins\nawait storage.storeCredential(credential);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n\tawait storage.storeCredential(credential);\n} catch (error) {\n\tif (String(error).includes(\"unavailable after SQLite corruption\")) {\n\t\tquarantineDbAndAlert(); // back up the corrupt db, notify operator\n\t}\n\tthrow error;\n}","preventionTips":["Monitor disk space and fsync-safe storage for the agent db path","Never copy or truncate the SQLite file while the app is running","Back up the credential db periodically so recovery is cheap","Alert on the latched damage log line so repair happens before writes are needed"],"tags":["sqlite","corruption","persistence","auth-storage"],"backgroundTag":"sqlite-database-corrupted","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}