{"record":{"id":"9e845743b1327838","repo":"sipeed/picoclaw","slug":"execute-s-w","errorCode":null,"errorMessage":"execute %s: %w","messagePattern":"execute (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/matrix/matrix.go","lineNumber":355,"sourceCode":"\tdb, err := sql.Open(sqliteDriver, connStr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open crypto database: %w\", err)\n\t}\n\tdb.SetMaxOpenConns(1)\n\tdb.SetMaxIdleConns(1)\n\n\t// Execute PRAGMA statements\n\t// This is equivalent to the \"sqlite3-fk-wal\" dialect used by cryptohelper\n\tpragmaStmts := []string{\n\t\t\"PRAGMA foreign_keys = ON\",\n\t\t\"PRAGMA journal_mode = WAL\",\n\t\t\"PRAGMA synchronous = NORMAL\",\n\t\t\"PRAGMA busy_timeout = 5000\",\n\t}\n\tfor _, pragma := range pragmaStmts {\n\t\tif _, err = db.ExecContext(ctx, pragma); err != nil {\n\t\t\t_ = db.Close()\n\t\t\treturn fmt.Errorf(\"execute %s: %w\", pragma, err)\n\t\t}\n\t}\n\n\t// Wrap with dbutil for dialect support\n\twrappedDB, err := dbutil.NewWithDB(db, sqliteDriver)\n\tif err != nil {\n\t\t_ = db.Close()\n\t\treturn fmt.Errorf(\"wrap database: %w\", err)\n\t}\n\n\tcryptoHelper, err := cryptohelper.NewCryptoHelper(c.client, []byte(c.config.CryptoPassphrase), wrappedDB)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create crypto helper: %w\", err)\n\t}\n\n\tif c.client.DeviceID == \"\" {\n\t\tresp, whoamiErr := c.client.Whoami(ctx)\n\t\tif whoamiErr != nil {","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/matrix/matrix.go#L337-L373","documentation":"Thrown by MatrixChannel.initCrypto (pkg/channels/matrix/matrix.go:355) while configuring the SQLite database that stores Matrix end-to-encryption state. Four PRAGMAs are executed in order (foreign_keys=ON, journal_mode=WAL, synchronous=NORMAL, busy_timeout=5000) via modernc.org/sqlite's database/sql driver; the message names the exact PRAGMA that failed. Any failure closes the DB and aborts channel startup, so encrypted Matrix messaging cannot run.","triggerScenarios":"Executing 'PRAGMA journal_mode = WAL' on a filesystem that does not support it (NFS/network volumes, some container overlayfs) causing SQLITE_IOERR; the file at the crypto DB path existing but not being a valid SQLite database ('file is not a database'); the crypto DB directory being read-only or owned by another user; a second process holding an exclusive lock on the DB file (busy_timeout=5000 is set later in the same loop, so it cannot rescue the earlier statements); the startup context being canceled mid-loop.","commonSituations":"Bot deployed in Docker/Kubernetes with the crypto DB path on a read-only or network-attached volume; a leftover corrupt crypto.db from a previous crashed run or from software that wrote a different format at the same path; running two bot instances against the same crypto DB path; permission changes (dir was created 0700 by root, bot now runs as non-root).","solutions":["Identify the failing PRAGMA from the message: 'journal_mode' points to filesystem/lock issues, 'foreign_keys' to a corrupt or non-SQLite file","Verify the DB file is valid SQLite: sqlite3 <cryptodb>/crypto.db 'PRAGMA integrity_check;' — if corrupt, delete the crypto DB directory and restart (the bot re-registers device keys; users must re-verify the new device)","Check the crypto DB directory is writable by the bot user and sits on a local filesystem, not NFS; move it if necessary","Ensure only one bot instance uses the crypto DB path at a time","Retry startup once environmental issues are fixed — the error is emitted at startup, not per-message"],"exampleFix":"# before: crypto db on a network volume\nMATRIX_CRYPTO_DB=/mnt/nfs/bot/crypto\n\n# after: crypto db on local writable storage\nMATRIX_CRYPTO_DB=/var/lib/bot/crypto\nmkdir -p /var/lib/bot/crypto && chown bot:bot /var/lib/bot/crypto","handlingStrategy":"validation","validationCode":"// pre-flight before starting the Matrix channel\nfunc cryptoDbUsable(dir string) error {\n\tif err := os.MkdirAll(dir, 0o700); err != nil {\n\t\treturn fmt.Errorf(\"crypto db dir: %w\", err)\n\t}\n\tprobe := filepath.Join(dir, \".write-probe\")\n\tif err := os.WriteFile(probe, []byte(\"ok\"), 0o600); err != nil {\n\t\treturn fmt.Errorf(\"crypto db dir not writable: %w\", err)\n\t}\n\t_ = os.Remove(probe)\n\tif dbFile := filepath.Join(dir, dbName); fileExists(dbFile) {\n\t\tdb, err := sql.Open(\"sqlite\", \"file:\"+dbFile+\"?mode=ro\")\n\t\tif err != nil { return err }\n\t\tdefer db.Close()\n\t\tif err := db.Ping(); err != nil {\n\t\t\treturn fmt.Errorf(\"crypto db not a valid sqlite file: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"// in Go, handle the startup error and distinguish transient vs permanent\nif err := matrixCh.Start(ctx); err != nil {\n\tvar pragmaErr *pragmaExecError // wrap and classify at your boundary\n\tif errors.As(err, &pragmaErr) && isLockError(pragmaErr.Unwrap()) {\n\t\tbackoff.Retry(startMatrix, 3) // transient SQLITE_BUSY\n\t} else {\n\t\tlog.Fatalf(\"matrix crypto db unusable: %v\", err) // corrupt/ro fs: fix environment\n\t}\n}","preventionTips":["Keep the crypto DB on a local writable volume; never NFS for WAL-mode SQLite","Run exactly one bot instance per crypto DB path","Health-check the crypto DB at deploy time (open + PRAGMA integrity_check) before swapping traffic","Back up the crypto DB alongside config so you never restore a corrupt snapshot"],"tags":["go","matrix","sqlite","crypto","e2ee","initialization","pragma"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}