{"record":{"id":"645791ebbcd4d60c","repo":"tinyhumansai/openhuman","slug":"open-chat-db-failed-for-full-day-read","errorCode":null,"errorMessage":"open chat.db failed for full-day read ({})","messagePattern":"open chat\\.db failed for full-day read \\((.+?)\\)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"app/src-tauri/src/imessage_scanner/chatdb.rs","lineNumber":114,"sourceCode":"        out.push(r?);\n    }\n    Ok(out)\n}\n\n/// Read ALL messages for a single `(chat_identifier, day)` slice, inclusive\n/// of the day boundary in Apple nanosecond epoch. Used to rebuild full-day\n/// transcripts before upserting memory docs — so tick-over-tick we always\n/// write the complete conversation for the day, never a partial delta\n/// that would overwrite prior content.\npub fn read_chat_day(\n    db_path: &Path,\n    chat_identifier: &str,\n    day_start_apple_ns: i64,\n    day_end_apple_ns: i64,\n    limit: usize,\n) -> anyhow::Result<Vec<Message>> {\n    let conn = open(db_path)\n        .map_err(|e| anyhow::anyhow!(\"open chat.db failed for full-day read ({})\", e))?;\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,\n          m.service          AS service\n        FROM message m\n        LEFT JOIN handle h ON h.ROWID = m.handle_id\n        LEFT JOIN chat_message_join cmj ON cmj.message_id = m.ROWID\n        LEFT JOIN chat c ON c.ROWID = cmj.chat_id","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src-tauri/src/imessage_scanner/chatdb.rs#L96-L132","documentation":"The same chat.db open failure as the incremental path, but raised inside read_chat_day() — the full-day transcript rebuild run before upserting memory docs so day-over-day writes are complete conversations, never partial deltas. Unlike error 290 this variant does not embed the Full Disk Access hint, only the raw SQLite error, so the remedy is less obvious from the message alone.","triggerScenarios":"The daily-rebuild pass hitting the same condition: FDA revoked between successful incremental scans; chat.db transiently locked or replaced by Messages.app maintenance; file missing on a fresh profile.","commonSituations":"Permissions reset by a macOS update mid-session; scanner racing a Messages.app db rebuild; the incremental path already failing (290) so the day rebuild fails identically.","solutions":["Apply the same fix as 290: grant/re-grant Full Disk Access to the app binary and restart","Verify the file is present and stable: ls -l ~/Library/Messages/chat.db","If intermittent, distinguish SQLITE_BUSY (transient lock — retry) from CANTOPEN/EPERM (TCC — fix FDA) using the appended error"],"exampleFix":"// before (app/src-tauri/src/imessage_scanner/chatdb.rs)\nlet conn = open(db_path).map_err(|e| anyhow::anyhow!(\"open chat.db failed for full-day read ({})\", e))?;\n\n// after — carry the same FDA remedy as the incremental reader\nlet conn = open(db_path).map_err(|e| anyhow::anyhow!(\n    \"open chat.db failed for full-day read ({}). Grant Full Disk Access to OpenHuman: \\\n     System Settings → Privacy & Security → Full Disk Access.\", e))?;","handlingStrategy":"validation","validationCode":"// share one guard with the incremental reader before the day rebuild\nmatch std::fs::metadata(db_path) {\n    Ok(_) => {}\n    Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {\n        log::warn!(\"[imessage] full-day read skipped: chat.db unreadable (Full Disk Access?)\");\n        return Ok(Vec::new());\n    }\n    Err(e) => return Err(anyhow::anyhow!(\"stat chat.db failed: {e}\")),\n}","typeGuard":null,"tryCatchPattern":"match chatdb::read_chat_day(&db, chat_id, start, end, limit) {\n    Ok(msgs) => Ok(msgs),\n    Err(e) if e.to_string().contains(\"open chat.db failed\") => {\n        log::warn!(\"[imessage] day rebuild deferred: {e}\"); Ok(Vec::new())  // retried next tick\n    }\n    Err(e) => Err(e),\n}","preventionTips":["route both readers through one FDA-aware preflight","map this failure to the same user-facing FDA prompt as error 290","distinguish SQLITE_BUSY (retry) from CANTOPEN/EPERM (fix FDA) in handling"],"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"}