Hmbown/CodeWhale · error

sub-agent transcript artifact header does not match agent {a

Error message

sub-agent transcript artifact header does not match agent {agent_id}

What it means

`load_subagent_transcript_artifact` validates the JSON header line against three invariants: kind must be "subagent_transcript_header", schema_version must equal SUBAGENT_TRANSCRIPT_ARTIFACT_SCHEMA_VERSION, and agent_id must equal the requested agent. Any mismatch refuses the load rather than deserializing a file that is not exactly this agent's artifact at this build's schema version.

Source

Thrown at crates/tui/src/tools/subagent/mod.rs:7595

/// manager state root.
pub fn load_subagent_transcript_artifact(
    state_root: &Path,
    agent_id: &str,
) -> Result<Vec<Message>> {
    let state_root = normalize_subagent_workspace(state_root);
    let path = checked_subagent_transcript_artifact_path(&state_root, agent_id)?;
    let raw = read_subagent_state_file(&state_root, &path)?;
    let mut lines = raw.lines();
    let header_line = lines
        .next()
        .ok_or_else(|| anyhow!("sub-agent transcript artifact is empty"))?;
    let header: Value = serde_json::from_str(header_line)?;
    if header.get("kind").and_then(Value::as_str) != Some("subagent_transcript_header")
        || header.get("schema_version").and_then(Value::as_u64)
            != Some(u64::from(SUBAGENT_TRANSCRIPT_ARTIFACT_SCHEMA_VERSION))
        || header.get("agent_id").and_then(Value::as_str) != Some(agent_id)
    {
        return Err(anyhow!(
            "sub-agent transcript artifact header does not match agent {agent_id}"
        ));
    }

    let mut messages = Vec::new();
    for line in lines.filter(|line| !line.trim().is_empty()) {
        let record: Value = serde_json::from_str(line)?;
        if record.get("kind").and_then(Value::as_str) != Some("message") {
            return Err(anyhow!("unknown sub-agent transcript artifact record"));
        }
        let index = record
            .get("index")
            .and_then(Value::as_u64)
            .and_then(|value| usize::try_from(value).ok())
            .ok_or_else(|| anyhow!("sub-agent transcript message is missing its index"))?;
        if index != messages.len() {
            return Err(anyhow!(
                "sub-agent transcript message index {index} does not follow {}",

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Confirm you are loading the artifact for the same agent_id you spawned (no normalization drift).
  2. After upgrading builds, delete stale artifacts and let them regenerate instead of reusing files from an old schema version.
  3. When forking or renaming agents, write new artifacts with a fresh header rather than copying files.
  4. Verify the header line's schema_version matches SUBAGENT_TRANSCRIPT_ARTIFACT_SCHEMA_VERSION in the running build.
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check the header before loading:
let raw = std::fs::read_to_string(&path)?;
let header: serde_json::Value = serde_json::from_str(raw.lines().next().unwrap())?;
let ok = header["kind"] == "subagent_transcript_header"
    && header["schema_version"].as_u64() == Some(u64::from(SUBAGENT_TRANSCRIPT_ARTIFACT_SCHEMA_VERSION))
    && header["agent_id"] == agent_id;
if !ok { /* wrong file or stale schema: regenerate */ }

Type guard

fn header_matches(header: &serde_json::Value, agent_id: &str, schema: u64) -> bool {
    header.get("kind").and_then(|v| v.as_str()) == Some("subagent_transcript_header")
        && header.get("schema_version").and_then(|v| v.as_u64()) == Some(schema)
        && header.get("agent_id").and_then(|v| v.as_str()) == Some(agent_id)
}

Try / catch

match load_subagent_transcript_artifact(&state_root, agent_id) {
    Err(e) if e.to_string().contains("does not match agent") => { /* check id + schema version, regenerate */ }
    r => r?,
}

Prevention

When it happens

Trigger: Loading with an agent_id whose artifact belongs to a different agent; artifact written by an older or newer build whose schema_version differs; a hand-edited or reformatted header; state dirs copied between installs.

Common situations: Version upgrades that bump the transcript schema; copying/migrating state directories between machines or builds; agent-id normalization differences (case, prefixes) between writer and reader.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/d24db2db089c246a. Report an issue: GitHub.