{"record":{"id":"d95cfe58e166aac6","repo":"tinyhumansai/openhuman","slug":"open-chat-db-failed-grant-full-disk-access-t","errorCode":null,"errorMessage":"open chat.db failed ({}). Grant Full Disk Access to OpenHuman: System Settings → Privacy & Security → Full Disk Access.","messagePattern":"open chat\\.db failed \\((.+?)\\)\\. Grant Full Disk Access to OpenHuman: System Settings → Privacy & Security → Full Disk Access\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"app/src-tauri/src/imessage_scanner/chatdb.rs","lineNumber":48,"sourceCode":"    pub chat_name: Option<String>,\n    pub service: Option<String>,\n}\n\n/// Open chat.db read-only. Returns a friendly error hint if Full Disk\n/// Access is not granted (the typical failure mode on first run).\nfn open(db_path: &Path) -> rusqlite::Result<Connection> {\n    Connection::open_with_flags(\n        db_path,\n        OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_PRIVATE_CACHE,\n    )\n}\n\n/// Read up to `limit` messages with `ROWID > since_rowid`, ordered by\n/// ROWID ascending. Joins across message / handle / chat_message_join /\n/// chat to produce one flat record per message.\npub fn read_since(db_path: &Path, since_rowid: i64, limit: usize) -> anyhow::Result<Vec<Message>> {\n    let conn = open(db_path).map_err(|e| {\n        anyhow::anyhow!(\n            \"open chat.db failed ({}). Grant Full Disk Access to OpenHuman: \\\n             System Settings → Privacy & Security → Full Disk Access.\",\n            e\n        )\n    })?;\n\n    let mut stmt = conn.prepare(\n        r#\"\n        SELECT\n          m.ROWID            AS rowid,\n          m.guid             AS guid,\n          m.text             AS text,\n          m.attributedBody   AS attributed_body,\n          m.date             AS date_ns,\n          m.is_from_me       AS is_from_me,\n          h.id               AS handle_id,\n          c.chat_identifier  AS chat_identifier,\n          c.display_name     AS chat_name,","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src-tauri/src/imessage_scanner/chatdb.rs#L30-L66","documentation":"Opening ~/Library/Messages/chat.db read-only (SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_PRIVATE_CACHE) failed; on macOS the overwhelmingly common cause is TCC denying the app Full Disk Access, so the error embeds the exact remediation path alongside the raw rusqlite error (unable to open database file / SQLITE_CANTOPEN).","triggerScenarios":"The process reading the db lacks Full Disk Access so macOS TCC denies open(2) on ~/Library/Messages/chat.db; alternatively the file is absent or relocated (fresh macOS profile, Messages-in-iCloud not yet synced down, OS upgrade).","commonSituations":"First scan after install before FDA was granted; FDA granted to a different binary than the one opening the db (launcher vs bundled helper); macOS re-evaluation of TCC identity after an app update; user revoked FDA later.","solutions":["Grant Full Disk Access to the exact app/binary that runs the scanner: System Settings → Privacy & Security → Full Disk Access → add/enable OpenHuman","Fully quit and restart the app afterwards — TCC is checked at open time, not retroactively","Confirm the db exists: ls -l ~/Library/Messages/chat.db; if missing, open Messages.app once so macOS creates/syncs it"],"exampleFix":"# before — scanner logs: open chat.db failed (unable to open database file). Grant Full Disk Access...\n# after — verify from the same user context, then restart the app\n sqlite3 \"file:$HOME/Library/Messages/chat.db?mode=ro\" 'select count(*) from message;'\n# a number ⇒ readable; an error ⇒ FDA still missing for that context","handlingStrategy":"validation","validationCode":"// preflight before scheduling the macOS scanner\nlet db = std::path::Path::new(&home).join(\"Library/Messages/chat.db\");\nif !db.exists() { return Ok(None); }  // nothing to scan\nmatch std::fs::metadata(&db) {\n    Ok(_) => {}\n    Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {\n        log::warn!(\"[imessage] chat.db unreadable — prompt for Full Disk Access\");\n        return Ok(None);\n    }\n    Err(e) => return Err(anyhow::anyhow!(\"stat chat.db failed: {e}\")),\n}","typeGuard":null,"tryCatchPattern":"match chatdb::read_since(&db, since, limit) {\n    Ok(msgs) => Ok(msgs),\n    Err(e) if e.to_string().contains(\"open chat.db failed\") => {\n        log::warn!(\"[imessage] chat.db unopenable ({e}) — likely FDA; disabling until granted\");\n        Ok(Vec::new())\n    }\n    Err(e) => Err(e),\n}","preventionTips":["surface an FDA consent prompt on first iMessage enable","treat unreadable chat.db as feature-unavailable, not a crash","re-check FDA after app updates since TCC identity can change"],"tags":["macos","tcc","full-disk-access","sqlite","imessage"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}