ruvnet/ruflo · error

build input escapes repository: ${path}

Error message

build input escapes repository: ${path}

What it means

Each declared build input is resolved to its realpath and required to stay under repoRoot (itself realpath'd). If the entry — or any symlink chain — resolves outside the repository, relative(repoRoot, real) starts with '..' (or is absolute) and the path is rejected. This prevents declared inputs from binding bytes the repository snapshot does not contain. Toolchain declarations are exempt by design: their paths may be absolute.

Source

Thrown at v3/@claude-flow/codex/src/harness/build-evidence.ts:161

 * declaration set is complete and does not sign or authorize a release.
 */
export function captureBuildEvidence(
  repoPath: string,
  sourceState: ExactSourceState,
  buildInputs: readonly BuildInputDeclaration[],
  toolchains: readonly ToolchainDeclaration[],
): BuildEvidence {
  const repoRoot = realpathSync(resolve(repoPath));
  if (captureRepositorySourceState(repoRoot).sourceStateId !== sourceState.sourceStateId) {
    throw new Error('Git-visible source state does not match the build evidence request');
  }
  const inputs = buildInputs.map((declaration): DeclaredBuildInput => {
    const path = normalizePath(declaration.path);
    const absolute = resolve(repoRoot, path);
    const real = realpathSync(absolute);
    const rel = relative(repoRoot, real);
    if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
      throw new Error(`build input escapes repository: ${path}`);
    }
    return { name: declaration.name, path, ...digestPath(real, true) };
  });
  const tools = toolchains.map((declaration): DeclaredToolchain => ({
    name: declaration.name,
    version: declaration.version,
    digest: digestPath(
      isAbsolute(declaration.path) ? declaration.path : resolve(repoRoot, declaration.path),
      true,
    ).digest,
  }));
  return createBuildEvidence(sourceState, inputs, tools);
}

export function recomputeBuildEvidence(
  repoPath: string,
  expected: BuildEvidence,
  toolchains: readonly ToolchainDeclaration[],

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Replace the symlink with the real file stored inside the repository (vendor it) so the input is self-contained
  2. Declare the external artifact as a ToolchainDeclaration instead — toolchain paths may be absolute and are hashed rather than embedded as inputs
  3. Repoint the symlink at a copy kept under the repo root

Example fix

// before
// config/shared.json is a symlink to ~/.config/shared.json (outside the repo)
const buildInputs = [{ name: 'shared-config', path: 'config/shared.json' }];

// after
// copy the file into the repo, then declare the real file
const buildInputs = [{ name: 'shared-config', path: 'config/shared.json' }]; // regular file now
// or declare it as a toolchain with an absolute path:
const toolchains = [{ name: 'shared-config', version: '1', path: '/home/me/.config/shared.json' }];
Defensive patterns

Strategy: validation

Validate before calling

import { realpathSync } from 'node:fs';
import { relative, isAbsolute, resolve, sep } from 'node:path';
function inputStaysInRepo(repoRoot: string, relativePath: string): boolean {
  const real = realpathSync(resolve(repoRoot, relativePath));
  const rel = relative(realpathSync(repoRoot), real);
  return rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
}

Try / catch

Catch and classify as a declaration defect: either vendor the file into the repo or move the declaration to toolchains (absolute paths allowed); retrying the same declaration always fails.

Prevention

When it happens

Trigger: A declared input that is a symlink into node_modules or a global cache outside the repo; a symlinked monorepo package directory pointing at a sibling checkout; a build step that created convenience links to /tmp artifacts which are then declared as inputs.

Common situations: pnpm/yarn-style symlinked dependency layouts inside the repo; developers symlinking shared config from a dotfiles repo; declaring a file inside a linked local package.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/864016e3c1d88030. Report an issue: GitHub.