Hmbown/CodeWhale · error

Compaction lifecycle task ended before acknowledgement

Error message

Compaction lifecycle task ended before acknowledgement

What it means

Raised when the oneshot acknowledgement channel for a compaction turn is dropped before the lifecycle task sends a value — the spawned compaction monitor ended (panicked, aborted, or exited) without accepting or rejecting the compaction. The RecvError is converted to this anyhow error so the caller fails fast instead of awaiting forever.

Solutions

  1. Retry the compaction; the store was kept consistent (turn either persisted or cleaned up).
  2. Check logs for a panic in the compaction lifecycle task at that time.
  3. Ensure the application is not shutting down the Tokio runtime while compactions are in flight.
  4. Report as a bug if reproducible — the lifecycle task must always send acceptance or rejection.
Defensive patterns

Strategy: retry

Try / catch

match runtime.compact_thread(&thread_id, req).await {
    Err(e) if e.to_string().contains("ended before acknowledgement") => {
        // lifecycle task died: check logs for panic, then retry compaction
    }
    r => r?,
}

Prevention

When it happens

Trigger: Awaiting `acceptance_rx` at the end of `compact_thread` after spawning the claimed-turn monitor with `ClaimedTurnKind::Compaction`, when the sender is dropped without a message.

Common situations: Panic inside the compaction lifecycle task (check stderr/logs); Tokio runtime shutdown mid-compaction; engine teardown/abort racing the compaction submission.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/ea3e6c8ee3aca6dd. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/runtime_threads.rs:10682

                current_thread.allow_shell,
                current_thread.trust_mode,
                policy.auto_approve(),
                policy.permission,
                configured_sandbox_mode,
            );
            engine.send_reserved_op(permit, op);
            touch_lru(&mut active.lru, thread_id);
            self.spawn_claimed_turn_monitor(
                turn.clone(),
                None,
                engine.clone(),
                ClaimedTurnKind::Compaction,
            )
        };

        acceptance_rx
            .await
            .map_err(|_| anyhow!("Compaction lifecycle task ended before acknowledgement"))?
            .map_err(anyhow::Error::msg)
    }

    #[cfg(test)]
    pub fn events_since(
        &self,
        thread_id: &str,
        since_seq: Option<u64>,
    ) -> Result<Vec<RuntimeEventRecord>> {
        self.store.events_since(thread_id, since_seq)
    }

    pub(crate) async fn events_since_async(
        &self,
        thread_id: &str,
        since_seq: Option<u64>,
    ) -> Result<Vec<RuntimeEventRecord>> {
        // Startup recovery deliberately queues terminal receipts until an

View on GitHub (pinned to 73e0f67d83)