Hmbown/CodeWhale · error · anyhow::Error

sub-agent state path must be a regular file

Error message

sub-agent state path must be a regular file: {}

What it means

When reading a sub-agent state file, the library takes `fs::symlink_metadata` and requires the entry to be a regular file; symlinks, directories, and special files are rejected even if reading through them would succeed. This guarantees state contents come from the real file inside the state root, not an alias.

Solutions

  1. Replace the symlink/directory with a regular file containing the state (copy the target's contents).
  2. Delete the unexpected entry and let the library recreate the state file.
  3. Configure dotfile managers to copy rather than symlink files under the state root.
  4. Verify container/volume setups so the state path is a plain file.

Example fix

// before: state file is a symlink
state/session.json -> /mnt/shared/session.json
// after
rm state/session.json
cp /mnt/shared/session.json state/session.json
Defensive patterns

Strategy: validation

Validate before calling

use std::fs;
fn readable_regular_state_file(p: &std::path::Path) -> bool {
    fs::symlink_metadata(p).map(|m| m.file_type().is_file()).unwrap_or(false)
}
// guard before reading: readable_regular_state_file(&state_path)

Try / catch

match read_state(root, path) {
    Ok(contents) => contents,
    Err(e) if e.to_string().contains("must be a regular file") => {
        // entry is a symlink/dir: restore a real file or accept defaults
        Default::default()
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling a sub-agent state read API where the target path is a symlink, a directory, or a special file — `file_type().is_symlink() || !file_type().is_file()`.

Common situations: The state file was replaced by a symlink to a shared/dotfile-managed location; a directory was created at the expected file path; a FIFO or device node occupies the path in a container.

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/7992441cba790a83. Report an issue: GitHub.

Appendix: source

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

                "sub-agent state path must not traverse symlinks: {}",
                current.display()
            ));
        }
    }
    Ok(())
}

/// Maximum bytes read from a sub-agent state or transcript artifact file.
/// Generous for long session histories; anything larger is corrupt.
const MAX_SUBAGENT_STATE_BYTES: u64 = 16 * 1024 * 1024;

fn read_subagent_state_file(state_root: &Path, path: &Path) -> Result<String> {
    let state_root = normalize_subagent_workspace(state_root);
    reject_root_relative_symlinks(&state_root, path)?;
    let metadata = fs::symlink_metadata(path)?;
    let file_type = metadata.file_type();
    if file_type.is_symlink() || !file_type.is_file() {
        return Err(anyhow!(
            "sub-agent state path must be a regular file: {}",
            path.display()
        ));
    }

    let file = open_subagent_state_file(path)?;
    let mut raw = String::new();
    file.take(MAX_SUBAGENT_STATE_BYTES + 1)
        .read_to_string(&mut raw)?;
    if raw.len() as u64 > MAX_SUBAGENT_STATE_BYTES {
        return Err(anyhow!(
            "sub-agent state file {} exceeds the 16 MiB limit",
            path.display()
        ));
    }
    Ok(raw)
}

View on GitHub (pinned to 73e0f67d83)