{"record":{"id":"7e3b09c68c633b99","repo":"affaan-m/ECC","slug":"db-writer-acknowledgement-dropped","errorCode":null,"errorMessage":"DB writer acknowledgement dropped","messagePattern":"DB writer acknowledgement dropped","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ecc2/src/session/runtime.rs","lineNumber":76,"sourceCode":"    }\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()),\n                    None => Err(open_error","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/ecc2/src/session/runtime.rs#L58-L94","documentation":"Sent when the oneshot ack channel is dropped before a result arrives — i.e. the DB writer task terminated mid-operation (panic, early return on open failure) without sending Ok/Err. Unlike 'channel closed' (send failed), here the message was delivered but the reply never came.","triggerScenarios":"run_db_writer task panics while processing the message, or exits after StateStore::open fails and drops rx/ack_tx without acking pending messages.","commonSituations":"Unreadable/corrupt session DB causing writer startup abort; panic inside the write path; process shutting down and aborting the writer task.","solutions":["Restart the session runtime to spawn a fresh DB writer","Investigate the writer's open_error / session stderr log for the underlying open or panic cause","Add a fallback timeout on ack_rx.await so callers fail fast with context"],"exampleFix":"// before\nmatch ack_rx.await { ... Err(_) => Err(anyhow::anyhow!(\"DB writer acknowledgement dropped\")) }\n// after\nmatch tokio::time::timeout(Duration::from_secs(10), ack_rx).await {\n    Ok(Ok(Ok(()))) => Ok(()),\n    Ok(Ok(Err(e))) => Err(anyhow::anyhow!(e)),\n    Ok(Err(_)) | Err(_) => Err(anyhow::anyhow!(\"DB writer acknowledgement dropped or timed out\")),\n}","handlingStrategy":"try-catch","validationCode":"if db_writer.is_closed() {\n    anyhow::bail!(\"writer already terminated; acks will never arrive\");\n}","typeGuard":null,"tryCatchPattern":"match db_writer.update_pid(pid).await {\n    Err(e) if e.to_string().contains(\"acknowledgement dropped\") => {\n        eprintln!(\"DB writer died mid-write; check session stderr log and restart runtime\");\n    }\n    Err(e) => return Err(e),\n    Ok(()) => (),\n}","preventionTips":["Add a timeout around ack awaits to fail fast","Log writer panics with catch_unwind/JoinHandle inspection","Validate the DB opens successfully before starting the writer loop"],"tags":["rust","tokio","oneshot","channel-closed","database"],"backgroundTag":"broken-pipe","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"}