{"record":{"id":"c5eb3beaa59d0163","repo":"affaan-m/ECC","slug":"error-runtime","errorCode":null,"errorMessage":"error","messagePattern":"error","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ecc2/src/session/runtime.rs","lineNumber":75,"sourceCode":"            .await\n    }\n\n    async fn touch_heartbeat(&self) -> Result<()> {\n        self.send(|ack| DbMessage::TouchHeartbeat { ack }).await\n    }\n\n    async fn send<F>(&self, build: F) -> Result<()>\n    where\n        F: FnOnce(oneshot::Sender<DbAck>) -> DbMessage,\n    {\n        let (ack_tx, ack_rx) = oneshot::channel();\n        self.tx\n            .send(build(ack_tx))\n            .map_err(|_| anyhow::anyhow!(\"DB writer channel closed\"))?;\n\n        match ack_rx.await {\n            Ok(Ok(())) => Ok(()),\n            Ok(Err(error)) => Err(anyhow::anyhow!(error)),\n            Err(_) => Err(anyhow::anyhow!(\"DB writer acknowledgement dropped\")),\n        }\n    }\n}\n\nfn run_db_writer(db_path: PathBuf, session_id: String, mut rx: mpsc::UnboundedReceiver<DbMessage>) {\n    let (opened, open_error) = match StateStore::open(&db_path) {\n        Ok(db) => (Some(db), None),\n        Err(error) => (None, Some(error.to_string())),\n    };\n\n    while let Some(message) = rx.blocking_recv() {\n        match message {\n            DbMessage::UpdateState { state, ack } => {\n                let result = match opened.as_ref() {\n                    Some(db) => db\n                        .update_state(&session_id, &state)\n                        .map_err(|error| error.to_string()),","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/ecc2/src/session/runtime.rs#L57-L93","documentation":"After the DB message is queued, send awaits a oneshot ack from the writer. Ok(Err(error)) means the writer received the message but the underlying DB operation failed; the inner error string is re-wrapped verbatim as anyhow \"error\"-style message. This is a propagated persistence failure, not a channel failure.","triggerScenarios":"run_db_writer executes the write against SQLite and returns Err — disk full, schema mismatch, locked database, constraint violation — and the ack carries that error back to update_state/update_pid/append_output_line/touch_heartbeat.","commonSituations":"SQLite file locked by another process (concurrent CLI inspecting the session); disk quota exceeded; DB schema version drift after an upgrade.","solutions":["Inspect the inner error text (it is embedded in this error) to find the actual DB failure","Retry the write after the lock holder releases the database","Back up/repair or recreate the session DB if the schema is corrupted"],"exampleFix":"// before\nErr(anyhow::anyhow!(error))\n// after\nErr(anyhow::anyhow!(\"DB write failed: {error}\")) // preserve cause in message","handlingStrategy":"retry","validationCode":"// check DB writability before writes\nlet f = std::fs::OpenOptions::new().append(true).open(&db_path)?;","typeGuard":null,"tryCatchPattern":"match db_writer.update_state(state).await {\n    Err(e) if is_db_error(&e) => {\n        tokio::time::sleep(Duration::from_millis(250)).await;\n        db_writer.update_state(state).await?; // retry once after transient lock\n    }\n    r => r?,\n}","preventionTips":["Avoid opening the session SQLite with other tools while the session runs","Watch disk space/quotas on the state directory","Keep schema versions in sync between CLI and runtime"],"tags":["rust","database","sqlite","write-failure"],"backgroundTag":"database-write-failed","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}