chenhg5/cc-connect · error

path escapes workspace base directory

Error message

path escapes workspace base directory

What it means

resolveLocalDirPath rejects resolved paths that fall outside the configured workspace baseDir. After cleaning and symlink-evaluating both the resolved target and the base directory, any path that is not the baseDir itself or beneath it is refused. This is a deliberate security guard against ../ traversal escaping the workspace sandbox.

Source

Thrown at core/engine.go:16996

		if err != nil {
			return "", fmt.Errorf("cannot resolve home directory: %w", err)
		}
		dirPath = filepath.Join(home, dirPath[2:])
	} else if !filepath.IsAbs(dirPath) {
		dirPath = filepath.Join(baseDir, dirPath)
	}
	cleaned := filepath.Clean(dirPath)
	resolved, err := filepath.EvalSymlinks(cleaned)
	if err != nil {
		resolved = cleaned
	}
	if baseDir != "" && !filepath.IsAbs(target) && !strings.HasPrefix(target, "~") {
		cleanBase := filepath.Clean(baseDir)
		if evalBase, err := filepath.EvalSymlinks(cleanBase); err == nil {
			cleanBase = evalBase
		}
		if !strings.HasPrefix(resolved, cleanBase+string(filepath.Separator)) && resolved != cleanBase {
			return "", fmt.Errorf("path escapes workspace base directory")
		}
	}
	return resolved, nil
}

// looksLikeLocalDir returns true if the string looks like a local directory
// path (absolute path, home-relative, dot-relative, or a bare name that
// doesn't look like a URL). Slash commands like /dir are not local dirs.
func looksLikeLocalDir(s string) bool {
	if s == "" {
		return false
	}
	if strings.Contains(s, "://") || strings.Contains(s, "@") {
		return false
	}
	if strings.HasPrefix(s, "~/") || s == "~" || strings.HasPrefix(s, "./") || strings.HasPrefix(s, "../") {
		return true
	}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Use a path inside baseDir, or change baseDir in config.toml to the parent directory you actually need
  2. Remove '..' segments from the target and specify it relative to baseDir
  3. Use an absolute path and adjust baseDir policy if absolute targets are intended
  4. Remove or re-point symlinks that resolve outside the workspace

Example fix

// before: escapes baseDir (~/workspaces)
target = "../other-repo"
// after: stays inside baseDir
target = "other-repo"
Defensive patterns

Strategy: validation

Validate before calling

func insideBase(target, baseDir string) bool {
    abs, err := filepath.Abs(target)
    if err != nil { return false }
    base, err := filepath.Abs(baseDir)
    if err != nil { return false }
    rel, err := filepath.Rel(base, abs)
    return err == nil && !strings.HasPrefix(rel, "..")
}
// call before resolveLocalDirPath

Try / catch

resolved, err := resolveLocalDirPath(userTarget, baseDir)
if err != nil {
    if strings.Contains(err.Error(), "escapes workspace base directory") {
        return fmt.Errorf("refusing path outside workspace: %s", userTarget)
    }
    return err
}

Prevention

When it happens

Trigger: Calling resolveLocalDirPath with a relative target such as "../other/repo" (or any path whose cleaned resolution lands outside baseDir) while baseDir is non-empty, target is relative, and target does not start with '~'.

Common situations: Configured workspace directory accidentally contains ".." segments; symlink inside the workspace pointing to an outside directory resolved via EvalSymlinks; user typo'd a sibling directory path; hostile input from a chat message used as a directory target.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/1bd41561658b0e5a. Report an issue: GitHub.