Hmbown/CodeWhale · error
unknown sub-agent transcript artifact record
Error message
unknown sub-agent transcript artifact record
What it means
While parsing lines after the header, `load_subagent_transcript_artifact` found a line whose JSON "kind" field is not "message". Only message records are valid in a transcript artifact; any other record kind (or a stray JSON line from another writer) aborts the load.
Source
Thrown at crates/tui/src/tools/subagent/mod.rs:7604
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 {}",
messages.len()
));
}
let message = serde_json::from_value::<Message>(
record
.get("message")
.cloned()
.ok_or_else(|| anyhow!("sub-agent transcript record is missing its message"))?,
)?;View on GitHub (pinned to 0c42157ee5)
Solutions
- Inspect the artifact for the offending line (first line after the header whose kind != "message") and remove or fix it.
- Ensure only the sub-agent persister writes transcript artifacts; move logs elsewhere.
- Regenerate the artifact by re-running the sub-agent if manual repair is impractical.
Defensive patterns
Strategy: try-catch
Validate before calling
// Scan artifact lines for foreign records before loading:
for (i, line) in raw.lines().enumerate().skip(1) {
if let Ok(v) = serde_json::from_str::<serde_json::Value>(line) {
if v["kind"].as_str() != Some("message") { /* line {i} is foreign: fix or remove */ }
}
} Type guard
fn line_is_message_record(line: &str) -> bool {
serde_json::from_str::<serde_json::Value>(line)
.map(|v| v.get("kind").and_then(|k| k.as_str()) == Some("message"))
.unwrap_or(false)
} Try / catch
match load_subagent_transcript_artifact(&state_root, agent_id) {
Err(e) if e.to_string().contains("unknown sub-agent transcript artifact record") => { /* locate + strip foreign line, retry once */ }
r => r?,
} Prevention
- Only the sub-agent persister writes artifact files.
- Keep logs and tool output out of the state root's artifact paths.
- Validate artifacts with a dry parse after any manual maintenance.
When it happens
Trigger: A JSONL line appended by a different producer (structured logs, tool output) into the artifact; mixed-format artifacts from a partial migration; hand-appended diagnostic records.
Common situations: State files shared with logging output; scripts that append to files under the state root; artifact formats changed between versions leaving old-style records in place.
Related errors
- sub-agent transcript artifact is empty
- sub-agent transcript history shrank from {} to {} messages
- sub-agent transcript artifact header does not match agent {a
- 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/c0498d7ff9b2f202.
Report an issue: GitHub.