ruvnet/ruflo · error
build evidence path is not a file or symlink: ${path}
Error message
build evidence path is not a file or symlink: ${path} What it means
To hash a declared build input, digestPath lstat's the (realpath-resolved) entry and reads content only for regular files and symlinks — a symlink contributes its target string as content. Any other filesystem node (directory, fifo, socket, device) yields no content and throws this error. A missing path fails earlier with the underlying ENOENT from lstatSync, not this error.
Source
Thrown at v3/@claude-flow/codex/src/harness/build-evidence.ts:90
) {
throw new Error(`unsafe build input path: ${value}`);
}
return path;
}
function sha256(value: string): string {
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
}
function digestPath(path: string, followSymlink: boolean): { digest: string; bytes: number } {
const resolved = followSymlink ? realpathSync(path) : path;
const stat = lstatSync(resolved);
const content = stat.isSymbolicLink()
? Buffer.from(readlinkSync(resolved), 'utf8')
: stat.isFile()
? readFileSync(resolved)
: undefined;
if (!content) throw new Error(`build evidence path is not a file or symlink: ${path}`);
return {
digest: `sha256:${createHash('sha256').update(content).digest('hex')}`,
bytes: content.byteLength,
};
}
/**
* Bind explicitly declared non-Git inputs and toolchains to one Git-visible
* source state. This does not claim completeness; policy decides which
* declarations are required for a release profile.
*/
export function createBuildEvidence(
sourceState: ExactSourceState,
buildInputs: readonly DeclaredBuildInput[],
toolchains: readonly DeclaredToolchain[],
): BuildEvidence {
const inputs = buildInputs.map((input) => ({
name: requireText(input.name, 'build input name'),View on GitHub (pinned to fa13ee4ad6)
Solutions
- Point the declaration at a concrete file — a manifest, archive, or lockfile — rather than a directory
- If the artifact is a directory, first archive or manifest it and declare that file
- For executables or images outside the repo, declare them as toolchains instead — toolchain paths may be absolute
Example fix
// before
const buildInputs = [{ name: 'dist', path: 'dist' }]; // directory
// after
const buildInputs = [{ name: 'dist-manifest', path: 'dist/manifest.json' }]; Defensive patterns
Strategy: validation
Validate before calling
import { lstatSync } from 'node:fs';
function isHashableInput(absolutePath: string): boolean {
const stat = lstatSync(absolutePath);
return stat.isFile() || stat.isSymbolicLink();
} Prevention
- Declare files, never directories; archive or manifest directory artifacts first
- Run capture from a clean checkout without dev servers holding sockets in the tree
- Remember missing paths fail with ENOENT — distinguish that from this not-a-file error
When it happens
Trigger: Declaring a directory as a build input (e.g. 'dist' or 'node_modules'); a path that is a fifo or socket created by a running dev server; a symlink chain whose final target is a directory.
Common situations: Declaring folder-shaped artifacts such as 'dist' or 'build' instead of a concrete file; running captureBuildEvidence while a dev server holds socket files inside the repo; symlink chains that resolve to a directory.
Related errors
- ${label} must be non-empty
- ${label} must be a canonical sha256 digest
- duplicate declared build input
- duplicate declared toolchain
- unsafe build input path: ${value}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/e335a7d3fd625059.
Report an issue: GitHub.