{"record":{"id":"5bf04b04f8bb79bc","repo":"sipeed/picoclaw","slug":"enable-foreign-keys-w","errorCode":null,"errorMessage":"enable foreign keys: %w","messagePattern":"enable foreign keys: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/channels/whatsapp_native/whatsapp_native.go","lineNumber":111,"sourceCode":"\tc.reconnecting = false\n\tc.reconnectMu.Unlock()\n\n\tif err := os.MkdirAll(c.storePath, 0o700); err != nil {\n\t\treturn fmt.Errorf(\"create session store dir: %w\", err)\n\t}\n\n\tdbPath := filepath.Join(c.storePath, whatsappDBName)\n\tconnStr := \"file:\" + dbPath + \"?_foreign_keys=on\"\n\n\tdb, err := sql.Open(sqliteDriver, connStr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open whatsapp store: %w\", err)\n\t}\n\tdb.SetMaxOpenConns(1)\n\tdb.SetMaxIdleConns(1)\n\tif _, err = db.ExecContext(ctx, \"PRAGMA foreign_keys = ON\"); err != nil {\n\t\t_ = db.Close()\n\t\treturn fmt.Errorf(\"enable foreign keys: %w\", err)\n\t}\n\n\twaLogger := waLog.Stdout(\"WhatsApp\", \"WARN\", true)\n\tcontainer := sqlstore.NewWithDB(db, sqliteDriver, waLogger)\n\tif err = container.Upgrade(ctx); err != nil {\n\t\t_ = db.Close()\n\t\treturn fmt.Errorf(\"open whatsapp store: %w\", err)\n\t}\n\n\tdeviceStore, err := container.GetFirstDevice(ctx)\n\tif err != nil {\n\t\t_ = container.Close()\n\t\treturn fmt.Errorf(\"get device store: %w\", err)\n\t}\n\n\tclient := whatsmeow.NewClient(deviceStore, waLogger)\n\n\t// Create runCtx/runCancel BEFORE registering event handler and starting","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/whatsapp_native/whatsapp_native.go#L93-L129","documentation":"Returned by WhatsAppNativeChannel.Start when the 'PRAGMA foreign_keys = ON' statement fails on the freshly opened sqlite session database. Because db.SetMaxOpenConns(1) forces the first real connection here, this is where file-open problems surface: corrupt database file, unreadable/unwritable path, disk I/O errors, or a locked database held by another process.","triggerScenarios":"Start(ctx) reaches ExecContext('PRAGMA foreign_keys = ON') and sqlite cannot open/read the store .db file - e.g. the file is corrupt, another picoclaw/whatsmeow instance holds it, the directory disappeared, or the disk is full/read-only.","commonSituations":"Two processes configured with the same storePath (second one fails); store corrupted after a power loss; NFS-mounted store with locking problems; disk full; store file owned by root after running once as root, then as a normal user.","solutions":["Ensure only one process uses this storePath; stop the other instance or give each its own path.","Check file permissions/ownership of the store .db and its directory for the service user.","Run sqlite3 PRAGMA integrity_check on the store file if corruption is suspected; back up and recreate if corrupt.","Free disk space / remount read-write if the volume is full or ro."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if _, err := os.Stat(dbPath); err == nil {\n    // pre-existing store: ensure exclusive access\n    lock, _ := os.OpenFile(dbPath+\".probe\", os.O_CREATE|os.O_EXCL, 0o600)\n    if lock != nil { _ = lock.Close(); _ = os.Remove(dbPath + \".probe\") }\n}\nif fi, err := os.Stat(filepath.Dir(dbPath)); err != nil || !fi.IsDir() {\n    return errors.New(\"store directory missing or invalid\")\n}","typeGuard":null,"tryCatchPattern":"if err := ch.Start(ctx); err != nil {\n    if strings.Contains(err.Error(), \"enable foreign keys\") {\n        // first real sqlite touch failed: check locks, permissions, corruption, disk space\n    }\n}","preventionTips":["Give each channel instance a dedicated storePath.","Run sqlite3 PRAGMA integrity_check during maintenance windows.","Monitor disk space and keep the store volume writable."],"tags":["whatsapp","sqlite","pragma","corruption","file-lock"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}