{"record":{"id":"3d99a74b723f2066","repo":"affaan-m/ECC","slug":"db-writer-channel-closed","errorCode":null,"errorMessage":"DB writer channel closed","messagePattern":"DB writer channel closed","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"ecc2/src/session/runtime.rs","lineNumber":71,"sourceCode":"    }\n\n    async fn append_output_line(&self, stream: OutputStream, line: String) -> Result<()> {\n        self.send(|ack| DbMessage::AppendOutputLine { stream, line, ack })\n            .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 } => {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/session/runtime.rs#L53-L89","documentation":"The DbRuntime sends database write commands through an mpsc channel to a dedicated DB writer task. If the channel's receiver is dropped — meaning the writer task has exited or panicked — send() fails and the function returns this error. All subsequent DB operations for this session are broken because there is no writer to process them.","triggerScenarios":"The DB writer task (run_db_writer) has exited due to a panic, DB open failure, or runtime shutdown, and a DbRuntime method (touch_heartbeat, etc.) attempts to send a message through the closed channel.","commonSituations":"DB writer task panicked during a write operation (e.g., SQLite locked, disk full). Tokio runtime was shut down while sessions are still active. DB file was deleted or moved while the writer was running.","solutions":["Check logs from run_db_writer for panic or DB open errors","Verify the DB file path is accessible and not locked by another process","Ensure the Tokio runtime is not being shut down prematurely while sessions are active","Restart the session if the writer task has died — the channel cannot be reopened","Monitor DB writer task health and abort sessions whose writer has failed"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match db_runtime.touch_heartbeat().await {\n    Ok(()) => Ok(()),\n    Err(e) if e.to_string().contains(\"DB writer channel closed\") => {\n        tracing::error!(\"DB writer task has died; session DB operations are broken\");\n        Err(e)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Monitor DB writer task health and abort sessions whose writer has failed","Ensure the Tokio runtime is not shut down while sessions are active","Check DB file accessibility and disk space to prevent writer-task panics","Implement a writer-task supervisor that restarts the writer on failure"],"tags":["database","channel","runtime","mpsc","writer-task"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}