zeroclaw-labs/zeroclaw · error · anyhow::Error

grok_cli working_directory must identify a directory

Error message

grok_cli working_directory must identify a directory

What it means

The working_directory value resolved (std::fs::canonicalize succeeded, so it exists and is accessible) but does not identify a directory - is_dir() returned false. The ACP session boundary must be a directory, not a file, socket, or symlink-to-file.

Solutions

  1. Point at the directory containing the file instead
  2. Recreate or repair the expected directory
  3. After deploy scripts that move paths, re-validate config

Example fix

# before
working_directory = "/home/user/project/README.md"

# after
working_directory = "/home/user/project"
Defensive patterns

Strategy: validation

Validate before calling

fn working_directory_usable(v: &str) -> bool {
    std::fs::canonicalize(v.trim()).map(|p| p.is_dir()).unwrap_or(false)
}

Type guard

fn working_directory_usable(v: &str) -> bool {
    std::fs::canonicalize(v.trim()).map(|p| p.is_dir()).unwrap_or(false)
}

Prevention

When it happens

Trigger: Pointing working_directory at a regular file, a socket/fifo, or a symlink whose target is a file; a path that was a directory when configured and has since been replaced by a file.

Common situations: Copy-pasting a file path into the config; symlink swap during deployments; tempdirs recreated as files by other tooling.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/1f6fdd5482f3c3b7. Report an issue: GitHub.

Appendix: source

Thrown at crates/zeroclaw-providers/src/grok_cli.rs:499

        }
    }

    fn validate_working_directory(value: &str) -> anyhow::Result<PathBuf> {
        let trimmed = value.trim();
        if trimmed.is_empty() {
            anyhow::bail!(
                "grok_cli requires an explicit working_directory for the ACP session boundary"
            );
        }
        let path = Path::new(trimmed);
        if !path.is_absolute() {
            anyhow::bail!("grok_cli working_directory must be an absolute path");
        }
        let canonical = std::fs::canonicalize(path).map_err(|_| {
            anyhow::Error::msg("grok_cli working_directory does not exist or is inaccessible")
        })?;
        if !canonical.is_dir() {
            anyhow::bail!("grok_cli working_directory must identify a directory");
        }
        Ok(canonical)
    }

    fn validate_acp_stdout_limit(value: Option<usize>) -> anyhow::Result<usize> {
        let limit = value.unwrap_or(acp::DEFAULT_ACP_STDOUT_LIMIT_BYTES);
        if !(acp::MIN_ACP_STDOUT_LIMIT_BYTES..=acp::MAX_ACP_STDOUT_LIMIT_BYTES).contains(&limit) {
            anyhow::bail!(
                "grok_cli max_acp_stdout_bytes must be between {} and {} bytes",
                acp::MIN_ACP_STDOUT_LIMIT_BYTES,
                acp::MAX_ACP_STDOUT_LIMIT_BYTES
            );
        }
        Ok(limit)
    }

    fn normalize_and_validate_env_passthrough(names: Vec<String>) -> anyhow::Result<Vec<String>> {
        let mut normalized: Vec<String> = Vec::with_capacity(names.len());

View on GitHub (pinned to 88bb9c8533)