affaan-m/ECC · error

DB writer acknowledgement dropped

Error message

DB writer acknowledgement dropped

What it means

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.

Solutions

  1. Restart the session runtime to spawn a fresh DB writer
  2. Investigate the writer's open_error / session stderr log for the underlying open or panic cause
  3. Add a fallback timeout on ack_rx.await so callers fail fast with context

Example fix

// before
match ack_rx.await { ... Err(_) => Err(anyhow::anyhow!("DB writer acknowledgement dropped")) }
// after
match tokio::time::timeout(Duration::from_secs(10), ack_rx).await {
    Ok(Ok(Ok(()))) => Ok(()),
    Ok(Ok(Err(e))) => Err(anyhow::anyhow!(e)),
    Ok(Err(_)) | Err(_) => Err(anyhow::anyhow!("DB writer acknowledgement dropped or timed out")),
}
Defensive patterns

Strategy: try-catch

Validate before calling

if db_writer.is_closed() {
    anyhow::bail!("writer already terminated; acks will never arrive");
}

Try / catch

match db_writer.update_pid(pid).await {
    Err(e) if e.to_string().contains("acknowledgement dropped") => {
        eprintln!("DB writer died mid-write; check session stderr log and restart runtime");
    }
    Err(e) => return Err(e),
    Ok(()) => (),
}

Prevention

When it happens

Trigger: run_db_writer task panics while processing the message, or exits after StateStore::open fails and drops rx/ack_tx without acking pending messages.

Common situations: Unreadable/corrupt session DB causing writer startup abort; panic inside the write path; process shutting down and aborting the writer task.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/7e3b09c68c633b99. Report an issue: GitHub.

Appendix: source

Thrown at ecc2/src/session/runtime.rs:76

    }

    async fn touch_heartbeat(&self) -> Result<()> {
        self.send(|ack| DbMessage::TouchHeartbeat { ack }).await
    }

    async fn send<F>(&self, build: F) -> Result<()>
    where
        F: FnOnce(oneshot::Sender<DbAck>) -> DbMessage,
    {
        let (ack_tx, ack_rx) = oneshot::channel();
        self.tx
            .send(build(ack_tx))
            .map_err(|_| anyhow::anyhow!("DB writer channel closed"))?;

        match ack_rx.await {
            Ok(Ok(())) => Ok(()),
            Ok(Err(error)) => Err(anyhow::anyhow!(error)),
            Err(_) => Err(anyhow::anyhow!("DB writer acknowledgement dropped")),
        }
    }
}

fn run_db_writer(db_path: PathBuf, session_id: String, mut rx: mpsc::UnboundedReceiver<DbMessage>) {
    let (opened, open_error) = match StateStore::open(&db_path) {
        Ok(db) => (Some(db), None),
        Err(error) => (None, Some(error.to_string())),
    };

    while let Some(message) = rx.blocking_recv() {
        match message {
            DbMessage::UpdateState { state, ack } => {
                let result = match opened.as_ref() {
                    Some(db) => db
                        .update_state(&session_id, &state)
                        .map_err(|error| error.to_string()),
                    None => Err(open_error

View on GitHub (pinned to 8321021c54)