Hmbown/CodeWhale · error
Compaction coverage floor: last-round tool call
Error message
Compaction coverage floor: last-round tool call {id} was dropped; history was not replaced. What it means
The survival contract requires every tool call (tool_use) from the last round to survive compaction. Keeping a tool_result while summarizing away its tool_use leaves an orphaned result that providers reject outright, so a replacement dropping a tool_use id is rejected before it can replace history.
Solutions
- Carry the last-round tool_use blocks (and their assistant message) into the replacement unmodified.
- Ensure tool_use and its tool_result are kept or dropped as a pair — never one alone.
- Fix summarization boundaries so only pre-last-round turns are rewritten.
- Check `tool_use_ids` extraction covers all last-round messages.
Example fix
// before: orphaned result replacement = [summary, tool_result_msg]; // after: keep call+result pair replacement = [summary, tool_use_msg, tool_result_msg];
Defensive patterns
Strategy: validation
Validate before calling
fn keeps_tool_uses(last_round: &[Message], replacement: &[Message]) -> bool {
last_round.iter().flat_map(tool_use_ids)
.all(|id| replacement.iter().any(|m| has_tool_use_id(m, &id)))
} Try / catch
match compaction::apply_replacement(&replacement) {
Err(e) if e.to_string().contains("tool call") => keep_original_history(),
other => other,
} Prevention
- Never summarize away the assistant message holding last-round tool_use blocks
- Keep tool_use/tool_result pairs together in rewrites
- Add fixture coverage for tool-bearing last rounds
- Run the coverage-floor test suite after compaction changes
When it happens
Trigger: `validate_last_round_coverage` iterates last-round `tool_use_ids` and finds no message in the replacement carrying that tool_use id.
Common situations: A rewrite keeps tool results but summarizes the assistant message containing the tool calls; a partial rewrite drops the assistant tool_use block; provider-strict pairing means this must fail fast.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Compaction coverage floor: a last-round user message was…
- Compaction coverage floor
- Compaction coverage floor: last-round assistant output was…
- Compaction coverage floor: last-round tool result
- Compaction coverage floor: checkpoint receipt was dropped…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/1d1e78486953450e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/compaction/last_round.rs:393
}
for id in last_round.iter().copied().flat_map(tool_result_ids) {
if !replacement
.iter()
.any(|message| has_tool_result_id(message, &id))
{
anyhow::bail!(
"Compaction coverage floor: last-round tool result {id} was dropped; history was not replaced."
);
}
}
// The call, not just its result. Keeping a tool_result whose tool_use was
// summarized away leaves an orphaned result that providers reject outright.
for id in last_round.iter().copied().flat_map(tool_use_ids) {
if !replacement
.iter()
.any(|message| has_tool_use_id(message, &id))
{
anyhow::bail!(
"Compaction coverage floor: last-round tool call {id} was dropped; history was not replaced."
);
}
}
// Match the assistant's actual output. An existential "some assistant
// message survived" check passed on a replacement whose only assistant
// message was the summary the rewrite had just written.
for text in last_round.iter().copied().filter_map(assistant_text_of) {
if !survives(&text, replacement, assistant_text_of) {
anyhow::bail!(
"Compaction coverage floor: last-round assistant output was dropped; history was not replaced."
);
}
}
if last_round
.iter()
.any(|message| message.role.is_assistant_like())
&& !replacementView on GitHub (pinned to 73e0f67d83)