Hmbown/CodeWhale · error · anyhow::Error

sub-agent transcript artifact is not a regular file

Error message

sub-agent transcript artifact is not a regular file: {}

What it means

Before deleting a sub-agent transcript artifact, the code checks it with `fs::symlink_metadata` and requires the entry to be a regular file — not a symlink, directory, FIFO, or other special file. If the path exists but is not a plain file, deletion is refused to avoid following or clobbering unexpected filesystem objects.

Solutions

  1. Remove the symlink or directory at that path manually and let the library recreate a regular file.
  2. Point the sub-agent state root at a location where the artifact path is a plain file.
  3. If the symlink is intentional, copy the target's contents into a regular file at the artifact path first.
  4. Check that container/volume mounts are not substituting special file types at the state path.

Example fix

// before: transcript path is a symlink
ls -l ~/.codewhale/state/transcripts/abc.jsonl  # -> symlink
// after: replace with a regular file
rm ~/.codewhale/state/transcripts/abc.jsonl
cp /shared/abc.jsonl ~/.codewhale/state/transcripts/abc.jsonl
Defensive patterns

Strategy: validation

Validate before calling

use std::fs;
fn is_regular_file(p: &std::path::Path) -> bool {
    fs::symlink_metadata(p).map(|m| m.file_type().is_file()).unwrap_or(false)
}
// only call the delete/cleanup API when is_regular_file(path)

Try / catch

match cleanup_transcript(path) {
    Ok(removed) => { /* proceed */ }
    Err(e) if e.to_string().contains("not a regular file") => {
        // remove the symlink/dir yourself, then retry
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Attempting transcript cleanup on a path that resolves to a symlink or a directory instead of a regular file; the check `metadata.file_type().is_symlink() || !metadata.file_type().is_file()` fires.

Common situations: A user replaced the transcript file with a symlink to shared storage; a directory was created where the artifact should live; tmpfs or container volume mounts produced non-regular entries.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/b5a34e82248e57cd. Report an issue: GitHub.

Appendix: source

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

                .cloned()
                .ok_or_else(|| anyhow!("sub-agent transcript record is missing its message"))?,
        )?;
        messages.push(message);
    }
    Ok(messages)
}

fn remove_subagent_transcript_artifact(state_root: &Path, agent_id: &str) -> Result<bool> {
    let state_root = normalize_subagent_workspace(state_root);
    let path = checked_subagent_transcript_artifact_path(&state_root, agent_id)?;
    reject_root_relative_symlinks(&state_root, &path)?;
    let metadata = match fs::symlink_metadata(&path) {
        Ok(metadata) => metadata,
        Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(false),
        Err(err) => return Err(err.into()),
    };
    if metadata.file_type().is_symlink() || !metadata.file_type().is_file() {
        return Err(anyhow!(
            "sub-agent transcript artifact is not a regular file: {}",
            path.display()
        ));
    }
    fs::remove_file(path)?;
    Ok(true)
}

#[cfg(test)]
pub(crate) fn write_subagent_transcript_artifact_for_test(
    state_root: &Path,
    agent_id: &str,
    messages: &[Message],
) -> Result<PathBuf> {
    let mut writer = SubAgentTranscriptArtifactWriter::create(state_root, agent_id)?;
    writer.sync_messages(messages, true)?;
    Ok(writer.path)
}

View on GitHub (pinned to 73e0f67d83)