zed-industries/zed · error · anyhow::Error

`cd` directory was not in any root directory in the project.

Error message

`cd` directory {cd:?} was not in any root directory in the project.

What it means

When an agent terminal command includes `cd <dir>`, the terminal tool collects every worktree's `(root_name, root_dir)` (skipping single-file worktrees, since a file cannot be a cwd) and calls `resolve_cd_in_worktrees`. If the `cd` target is neither a worktree root name nor an absolute path under any root, it bails with this message. Absolute paths are classified with the project's `PathStyle`, not the host's, so a Windows host can target WSL/SSH POSIX paths — but they must still be inside a root.

Solutions

  1. Only `cd` to paths inside a currently open worktree, or to a worktree's root name.
  2. Open the additional folder in the Zed project (multi-root) so its root becomes resolvable.
  3. Use relative paths within the already-correct cwd instead of `cd`-ing out.
  4. For remote/WSL projects, use the project's POSIX-style absolute path, not the host's path.

Example fix

# agent terminal command
# before
cd /home/user/other-project && cargo test   # outside any root -> error
# after (open other-project as a second worktree first)
cd other-project && cargo test              # root name resolves
Defensive patterns

Strategy: validation

Validate before calling

// Before emitting a cd, verify the target is a root name or under a root dir:
let roots: Vec<(String, PathBuf)> = project.worktrees(cx).iter().filter_map(|wt| {
    let wt = wt.read(cx);
    Some((wt.root_name_str().to_string(), wt.root_dir()?.to_path_buf()))
}).collect();
let ok = roots.iter().any(|(name, dir)| cd == *name
    || normalize(&resolve(cd)).starts_with(dir));
if !ok { /* rewrite the command to stay inside a worktree */ }

Type guard

fn cd_resolves(cd: &str, roots: &[(String, PathBuf)]) -> bool {
    roots.iter().any(|(name, dir)| {
        cd == name || Path::new(cd).normalize().starts_with(dir)
    })
}

Try / catch

match run_terminal(cmd).await {
    Err(e) if e.to_string().contains("was not in any root directory") => {
        // re-issue without the cd, or open the folder as a worktree first
    }
    rest => rest,
}

Prevention

When it happens

Trigger: The model emits `cd /some/path/outside/the/project`, `cd ../sibling-repo`, or `cd <name>` where `<name>` is not any worktree root name; also when every open worktree is a single file so the roots list is empty.

Common situations: The agent assumes a broader filesystem than the project allows; the user has one folder open but asks the agent to work in another; monorepo subdirectories are fine but sibling repos are not.

Related errors


AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20). Data as JSON: /api/errors/e6785561b1084fe9. Report an issue: GitHub.

Appendix: source

Thrown at crates/agent/src/tools/terminal_tool.rs:1373

            None => Ok(None),
        }
    } else {
        let path_style = project.path_style(cx);
        let worktree_roots = project
            .worktrees(cx)
            .filter_map(|worktree| {
                let worktree = worktree.read(cx);
                // Skip single-file worktrees: a file can't be a working directory.
                let root_dir = worktree.root_dir()?;
                Some((worktree.root_name_str(), root_dir.to_path_buf()))
            })
            .collect::<Vec<_>>();

        if let Some(dir) = resolve_cd_in_worktrees(cd, path_style, &worktree_roots) {
            return Ok(Some(dir));
        }

        anyhow::bail!("`cd` directory {cd:?} was not in any root directory in the project.");
    }
}

/// Resolves a `cd` argument to an absolute worktree directory. `cd` may be a
/// worktree's root name or an absolute path to a worktree or a subdirectory
/// therein.
///
/// Absolute paths are classified with the project's [`PathStyle`] rather than
/// the host's, so an absolute POSIX path resolves correctly on a Windows host
/// driving a WSL/SSH project (#60040).
///
/// Both `cd` and the worktree roots are lexically normalized before prefix
/// matching. This resolves `.` and `..` components up front, so a path that
/// escapes a worktree does not have that worktree's root as a prefix and is
/// rejected (#60014). On Windows-style projects it also unifies `/` and `\`
/// separators, since models frequently write `C:/foo/bar` for a root stored
/// as `C:\foo\bar`.
fn resolve_cd_in_worktrees(

View on GitHub (pinned to f4178619ac)