Hmbown/CodeWhale · error
sub-agent transcript history shrank from {} to {} messages
Error message
sub-agent transcript history shrank from {} to {} messages What it means
The sub-agent transcript persister enforces an append-only invariant: `persisted_messages` counts the JSONL lines already on disk, and `sync_messages` errors when the incoming message slice is shorter than that count. Hitting it means the message history handed to the persister dropped messages that were already written — an integrity violation, not normal compaction. Treat it as a bug in history management or in a fork/replay path.
Source
Thrown at crates/tui/src/tools/subagent/mod.rs:7518
let relative_path = subagent_transcript_artifact_relative_path(agent_id);
let path = checked_subagent_transcript_artifact_path(&state_root, agent_id)?;
let header = json!({
"kind": "subagent_transcript_header",
"schema_version": SUBAGENT_TRANSCRIPT_ARTIFACT_SCHEMA_VERSION,
"agent_id": agent_id,
});
create_private_subagent_transcript(&state_root, &path, &json_line(&header)?)?;
Ok(Self {
state_root,
path,
relative_path,
persisted_messages: 0,
})
}
fn sync_messages(&mut self, messages: &[Message], durable: bool) -> Result<()> {
if messages.len() < self.persisted_messages {
return Err(anyhow!(
"sub-agent transcript history shrank from {} to {} messages",
self.persisted_messages,
messages.len()
));
}
let mut encoded = Vec::new();
for (index, message) in messages.iter().enumerate().skip(self.persisted_messages) {
encoded.extend(json_line(&json!({
"kind": "message",
"index": index,
"message": message,
}))?);
}
if !encoded.is_empty() || durable {
append_private_subagent_transcript(&self.state_root, &self.path, &encoded, durable)?;
}View on GitHub (pinned to 0c42157ee5)
Solutions
- Never remove or reorder messages passed to sync_messages — append only.
- If on-disk history must shrink, create a fresh transcript artifact (new header, persisted_messages reset to 0) instead of syncing a shorter slice.
- Audit compaction/fork paths: either preserve the full message count or fork before the first sync.
- After an upgrade, confirm persisted_messages is initialized from the existing artifact's line count, not from an empty default.
Example fix
// before: compaction trims history in place, then syncs let compacted = messages[len/2..].to_vec(); persister.sync_messages(&compacted, true)?; // Err 1202 // after: shrinking on disk requires a new artifact let fresh = TranscriptArtifact::create(state_root, agent_id, &header)?; fresh.sync_messages(&full_history, true)?; // full slice, append-only
Defensive patterns
Strategy: try-catch
Validate before calling
// Before syncing, assert the append-only invariant yourself:
debug_assert!(messages.len() >= persister.persisted_messages(),
"history would shrink: {} -> {}", persister.persisted_messages(), messages.len()); Type guard
fn history_is_append_only(persisted: usize, incoming: &[Message]) -> bool {
incoming.len() >= persisted
} Try / catch
if let Err(e) = persister.sync_messages(&messages, durable) {
if e.to_string().contains("history shrank") {
// integrity bug: do NOT truncate the file to match; log, keep the artifact,
// and rebuild the in-memory history from the artifact instead.
}
} Prevention
- Treat transcript vectors as append-only; never compact in place.
- Fork before the first sync, or create a fresh artifact when shrinking on disk.
- Initialize persisted_messages from the artifact's line count on reopen.
When it happens
Trigger: Calling sync_messages with a Message vec shorter than persisted_messages: in-place history compaction that trims old messages, a fork/checkpoint restore that rebuilds a truncated context, or a refactor that filters messages before syncing.
Common situations: Wiring context-window compaction into the sub-agent loop; resuming from checkpoints that reconstruct fewer messages than the transcript contains; changing message filtering between syncs.
Related errors
- sub-agent transcript artifact is empty
- sub-agent transcript artifact header does not match agent {a
- unknown sub-agent transcript artifact record
- sub-agent transcript message is missing its index
- sub-agent transcript message index {index} does not follow {
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/ab36915dde830a61.
Report an issue: GitHub.