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

Worktree path '{}' resolves outside the workspace or allowed

Error message

Worktree path '{}' resolves outside the workspace or allowed roots

What it means

Before creating a worktree, ensure_worktree_add_target_allowed resolves the target's parent directory (which must already exist, because canonicalize fails on missing paths), re-joins the file name, and asks SecurityPolicy::is_resolved_path_allowed whether the resolved target is inside the workspace root or one of the policy's allowed_roots (git_operations.rs:165-172; policy check at zeroclaw-config/src/policy.rs:3056). If the location is outside every allowlisted root, the add is refused.

Source

Thrown at crates/zeroclaw-tools/src/git_operations.rs:168

            ::zeroclaw_log::record!(
                WARN,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({
                        "parent": parent.display().to_string(),
                        "error": format!("{}", e),
                    })),
                "git_operations: cannot resolve worktree parent"
            );
            anyhow::Error::msg(format!(
                "Cannot resolve worktree parent '{}': {e}",
                parent.display()
            ))
        })?;
        let resolved_target = resolved_parent.join(file_name);

        if !self.security.is_resolved_path_allowed(&resolved_target) {
            anyhow::bail!(
                "Worktree path '{}' resolves outside the workspace or allowed roots",
                raw_path
            );
        }

        Ok(resolved_target)
    }

    fn ensure_worktree_remove_target_allowed(&self, raw_path: &str) -> anyhow::Result<PathBuf> {
        let candidate = self.candidate_path(raw_path)?;
        let resolved = candidate.canonicalize().map_err(|e| {
            ::zeroclaw_log::record!(
                WARN,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({
                        "raw_path": raw_path,
                        "error": format!("{}", e),

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Create worktrees under the workspace root (e.g. ".worktrees/name") — always allowed.
  2. Create the parent directory first so canonicalize succeeds, keeping it under the workspace.
  3. If an external location is genuinely required, add its canonical parent directory to the SecurityPolicy allowed_roots in zeroclaw config and retry.
  4. Pre-check with security.is_resolved_path_allowed on the caller side before issuing the add.

Example fix

// before
worktree(op: "add", path: "/tmp/feature-wt")
// -> Worktree path '/tmp/feature-wt' resolves outside the workspace or allowed roots

// after
worktree(op: "add", path: ".worktrees/feature-wt")
Defensive patterns

Strategy: validation

Validate before calling

// Verify the target would pass the policy before calling the tool.
let candidate = workspace.join(".worktrees").join(name);
std::fs::create_dir_all(candidate.parent().unwrap())?; // parent must exist
let resolved_parent = candidate.parent().unwrap().canonicalize()?;
let target = resolved_parent.join(candidate.file_name().unwrap());
if !security.is_resolved_path_allowed(&target) {
    return Err(format!("worktree target {target:?} outside allowed roots"));
}

Type guard

fn is_allowed_worktree_target(target: &std::path::Path, security: &SecurityPolicy) -> bool {
    security.is_resolved_path_allowed(target)
}

Try / catch

match git_tool.execute(params).await {
    Err(e) if e.to_string().contains("resolves outside the workspace or allowed roots") => {
        // create the worktree under the workspace instead and retry; widen
        // allowed_roots only as a deliberate, reviewed config change
    }
    r => r,
}

Prevention

When it happens

Trigger: Calling git_worktree add with a target outside the workspace, e.g. "/tmp/wt" or "~/wt" (broad default-forbidden roots include /home and /tmp per policy.rs comments); targeting a sibling directory of the workspace; targeting a path whose parent does not exist yet (fails first with 'Cannot resolve worktree parent'); running under a workspace_only security policy with no extra allowed_roots configured.

Common situations: Developers following the `git worktree add ../branch-x` convention inside a sandboxed agent workspace; CI sandboxes where the workspace is a subdirectory; policies tightened to workspace_only during a security review so previously fine locations stop working.

Related errors


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