{"record":{"id":"e335a7d3fd625059","repo":"ruvnet/ruflo","slug":"build-evidence-path-is-not-a-file-or-symlink-pa","errorCode":null,"errorMessage":"build evidence path is not a file or symlink: ${path}","messagePattern":"build evidence path is not a file or symlink: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/harness/build-evidence.ts","lineNumber":90,"sourceCode":"  ) {\n    throw new Error(`unsafe build input path: ${value}`);\n  }\n  return path;\n}\n\nfunction sha256(value: string): string {\n  return `sha256:${createHash('sha256').update(value).digest('hex')}`;\n}\n\nfunction digestPath(path: string, followSymlink: boolean): { digest: string; bytes: number } {\n  const resolved = followSymlink ? realpathSync(path) : path;\n  const stat = lstatSync(resolved);\n  const content = stat.isSymbolicLink()\n    ? Buffer.from(readlinkSync(resolved), 'utf8')\n    : stat.isFile()\n      ? readFileSync(resolved)\n      : undefined;\n  if (!content) throw new Error(`build evidence path is not a file or symlink: ${path}`);\n  return {\n    digest: `sha256:${createHash('sha256').update(content).digest('hex')}`,\n    bytes: content.byteLength,\n  };\n}\n\n/**\n * Bind explicitly declared non-Git inputs and toolchains to one Git-visible\n * source state. This does not claim completeness; policy decides which\n * declarations are required for a release profile.\n */\nexport function createBuildEvidence(\n  sourceState: ExactSourceState,\n  buildInputs: readonly DeclaredBuildInput[],\n  toolchains: readonly DeclaredToolchain[],\n): BuildEvidence {\n  const inputs = buildInputs.map((input) => ({\n    name: requireText(input.name, 'build input name'),","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/harness/build-evidence.ts#L72-L108","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst buildInputs = [{ name: 'dist', path: 'dist' }]; // directory\n\n// after\nconst buildInputs = [{ name: 'dist-manifest', path: 'dist/manifest.json' }];","handlingStrategy":"validation","validationCode":"import { lstatSync } from 'node:fs';\nfunction isHashableInput(absolutePath: string): boolean {\n  const stat = lstatSync(absolutePath);\n  return stat.isFile() || stat.isSymbolicLink();\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["build-evidence","filesystem","harness","validation"],"backgroundTag":"not-a-regular-file","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}