Hmbown/CodeWhale · error

sub-agent transcript artifact must have a parent directory

Error message

sub-agent transcript artifact must have a parent directory

What it means

`prepare_subagent_transcript_parent` requires the artifact path to have a parent directory (path.parent()); a degenerate path (empty or root-like) has none, so transcript creation is refused before any directory is made. Like 1211 this guards against malformed path construction rather than runtime state.

Source

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

    use std::os::unix::fs::OpenOptionsExt;

    fs::OpenOptions::new()
        .read(true)
        .custom_flags(libc::O_NOFOLLOW)
        .open(path)
        .map_err(Into::into)
}

#[cfg(not(unix))]
fn open_subagent_state_file(path: &Path) -> Result<fs::File> {
    fs::File::open(path).map_err(Into::into)
}

fn prepare_subagent_transcript_parent(state_root: &Path, path: &Path) -> Result<()> {
    reject_root_relative_symlinks(state_root, path)?;
    let parent = path
        .parent()
        .ok_or_else(|| anyhow!("sub-agent transcript artifact must have a parent directory"))?;
    fs::create_dir_all(parent)?;
    // Re-check after creation so a pre-existing component cannot redirect the
    // private transcript outside the state root.
    reject_root_relative_symlinks(state_root, path)?;
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        fs::set_permissions(parent, fs::Permissions::from_mode(0o700))?;
    }
    Ok(())
}

fn create_private_subagent_transcript(state_root: &Path, path: &Path, bytes: &[u8]) -> Result<()> {
    prepare_subagent_transcript_parent(state_root, path)?;
    let mut file = open_private_subagent_transcript(path, false)?;
    file.write_all(bytes)?;
    file.sync_all()?;
    Ok(())

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Construct artifact paths as state_root.join(transcripts_dir).join(file_name) with both parts non-empty.
  2. Reject empty directory components at the input boundary.
Defensive patterns

Strategy: validation

Validate before calling

let artifact = state_root.join("transcripts").join(format!("{agent_id}.jsonl"));
assert!(artifact.parent().is_some(), "artifact path needs a directory part");

Type guard

fn path_has_parent(p: &Path) -> bool {
    p.parent().map_or(false, |parent| !parent.as_os_str().is_empty())
}

Try / catch

match prepare_subagent_transcript_parent(&state_root, &path) {
    Err(e) if e.to_string().contains("parent directory") => { /* rebuild path under an explicit transcripts dir */ }
    r => r?,
}

Prevention

When it happens

Trigger: Building a transcript path from user/model input that collapses to an empty or root-level path, or a PathBuf constructed without joining under the state root.

Common situations: Templating that yields "" for the directory part; API misuse calling the persister with a bare file name string.

Related errors


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