coleam00/Archon · error

.archon/config.yaml worktree.path resolves outside the repo

Error message

.archon/config.yaml worktree.path resolves outside the repo root (got: ${trimmed} → ${resolved}).

What it means

Even after the textual `..`-segment check, resolveRepoLocalOverride re-resolves the configured `worktree.path` against the repo root using absolute paths and confirms the result stays inside the repo. This catches edge cases that normalize clean textually but still escape when joined (e.g. leading `./../` or symlink-like segments).

Source

Thrown at packages/isolation/src/providers/worktree.ts:139

    normalized === '..' ||
    normalized.startsWith('../') ||
    normalized.startsWith('..\\') ||
    normalized.includes('/../') ||
    normalized.includes('\\..\\')
  ) {
    throw new Error(
      `.archon/config.yaml worktree.path must stay within the repo (got: ${trimmed}). ` +
        'Remove any `..` segments.'
    );
  }

  // Double-check via resolved absolute paths — catches edge cases like a path that
  // normalizes clean but still escapes when joined (e.g. leading `./../` on some platforms).
  // Uses `path.sep` so the "is inside repoRoot" check works on Windows (\\) as well as POSIX (/).
  const resolved = resolve(repoRoot, normalized);
  const repoRootResolved = resolve(repoRoot);
  if (resolved !== repoRootResolved && !resolved.startsWith(repoRootResolved + sep)) {
    throw new Error(
      `.archon/config.yaml worktree.path resolves outside the repo root (got: ${trimmed} → ${resolved}).`
    );
  }

  return normalized;
}

export class WorktreeProvider implements IIsolationProvider {
  readonly providerType = 'worktree';

  constructor(private loadConfig: RepoConfigLoader = () => Promise.resolve(null)) {}

  /**
   * Create an isolated environment using git worktrees.
   *
   * Config is loaded exactly once here and threaded through the rest of the
   * `create()` call. A malformed `.archon/config.yaml` fails loudly at this
   * boundary rather than being swallowed — see CLAUDE.md "Fail Fast + Explicit

View on GitHub (pinned to 0773b97458)

Solutions

  1. Change `worktree.path` so it resolves to a directory inside the repository, e.g. `.worktrees`
  2. Use the global `~/.archon/config.yaml` `paths.worktrees` for an absolute outside-the-repo location
  3. Print `path.resolve(repoRoot, value)` locally to see where the configured value actually lands, then adjust

Example fix

// before
worktree:
  path: ./../archon-worktrees
// after
worktree:
  path: .archon-worktrees
Defensive patterns

Strategy: validation

Validate before calling

import { resolve, sep } from 'node:path';
export function isInsideRepo(candidate: string, repoRoot: string): boolean {
  const resolved = resolve(repoRoot, candidate);
  const root = resolve(repoRoot);
  return resolved === root || resolved.startsWith(root + sep);
}
// call before relying on the config: if (!isInsideRepo(cfg.worktree.path, repoRoot)) throw ...

Prevention

When it happens

Trigger: A `worktree.path` such as `./../outside` or any value whose `path.resolve(repoRoot, value)` result is neither equal to the resolved repo root nor prefixed by `repoRoot + path.sep`, on Windows or POSIX.

Common situations: Platform-specific separator confusion (`..\foo` on POSIX), copying configs between machines with different repo layouts, values that pass a naive string check but resolve outside the tree.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/0297ffd82d1d3bf6. Report an issue: GitHub.