{"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":null,"httpStatus":null,"severity":"error","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/8321021c54d670126ce3b2969d5deb880b4b0c2a/ecc2/src/session/runtime.rs#L53-L89","documentation":"SessionStateHandle::send forwards DB mutations over an mpsc channel to a dedicated writer task; if that task has exited, the UnboundedSender fails and this error is returned to update_state/update_pid/append_output_line/touch_heartbeat callers. It means the session's persistence pipeline is dead — no further DB writes will land.","triggerScenarios":"The run_db_writer task panicked or returned (e.g. fatal open error on the SQLite file) while the session handle is still used; writing DB messages after the writer was dropped at session teardown.","commonSituations":"Corrupt or locked session DB causing the writer to exit; calling touch_heartbeat/append_output_line after stop_session dropped the writer; DB directory deleted at runtime.","solutions":["Recreate/reopen the DB writer (restart the session runtime) — the current handle cannot recover","Guard callers so they stop issuing writes once the session is stopped/torn down","Check the writer's open_error / stderr log for why run_db_writer exited (disk full, permissions, lock)"],"exampleFix":"// before\ndb_writer.append_output_line(line).await?; // panics/errors if writer dropped\n// after\nif db_writer.is_closed() {\n    eprintln!(\"db writer unavailable; skipping output line\");\n} else {\n    db_writer.append_output_line(line).await?;\n}","handlingStrategy":"retry","validationCode":"if db_writer.is_closed() {\n    anyhow::bail!(\"db writer stopped; restart the session runtime before writing\");\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = db_writer.touch_heartbeat().await {\n    if e.to_string().contains(\"channel closed\") {\n        eprintln!(\"persistence pipeline dead; recreating writer...\");\n        db_writer = recreate_writer(&db_path, &session_id).await?;\n    } else { return Err(e); }\n}","preventionTips":["Recreate the writer after stop_session instead of reusing handles","Monitor run_db_writer exit reasons (open errors) early","Ensure the session DB directory exists and is writable at startup"],"tags":["rust","tokio","mpsc","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"}