{"record":{"id":"e7d05188e75411a5","repo":"affaan-m/ECC","slug":"refusing-to-read-a-non-file-at-filepath-multi-harness-setup","errorCode":null,"errorMessage":"Refusing to read a non-file at ${filePath}.","messagePattern":"Refusing to read a non-file at (.+?)\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/multi-harness-setup.js","lineNumber":88,"sourceCode":"    && left.size === right.size\n    && left.mtimeMs === right.mtimeMs\n    && left.ctimeMs === right.ctimeMs;\n}\n\nfunction readRegularFileSnapshot(filePath) {\n  const flags = fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0);\n  let descriptor;\n  try {\n    descriptor = fs.openSync(filePath, flags);\n  } catch (error) {\n    if (error && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) return null;\n    throw error;\n  }\n\n  try {\n    const before = fs.fstatSync(descriptor);\n    if (!before.isFile()) {\n      throw new Error(`Refusing to read a non-file at ${filePath}.`);\n    }\n    const content = fs.readFileSync(descriptor);\n    const after = fs.fstatSync(descriptor);\n    const finalPathStat = fs.lstatSync(filePath);\n    if (\n      finalPathStat.isSymbolicLink()\n      || !finalPathStat.isFile()\n      || !sameFileIdentity(before, after)\n      || !sameFileIdentity(after, finalPathStat)\n    ) {\n      throw new Error(`Refusing to read a file that changed during validation: ${filePath}.`);\n    }\n    return { content, stat: after };\n  } finally {\n    fs.closeSync(descriptor);\n  }\n}\n","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/multi-harness-setup.js#L70-L106","documentation":"readRegularFileSnapshot opens a file and, after fstat on the open descriptor, refuses to proceed if the opened object is not a regular file. This defends against reading directories, FIFOs, device files, or files swapped to non-regular types mid-read, which would produce garbage or block. The library deliberately fails closed rather than reading a non-regular inode.","triggerScenarios":"Passing a directory, named pipe, socket, or device path (or a symlink resolving to one) as a destination/state file path to snapshot/destination/classifyManagedOperation during guided install preview.","commonSituations":"Misconfigured target root pointing at a directory where a file was expected; a leftover FIFO or socket at the target path; a symlink redirected to /dev/null or a directory after a partially-failed install.","solutions":["Ensure the path refers to a regular file; if it is a directory, correct the configured path or remove the directory","Remove or replace any FIFO/socket/device node at the target path","Re-run the guided preview after fixing the filesystem state so the snapshot succeeds"],"exampleFix":"// before (statePath accidentally a directory)\nfs.mkdirSync(statePath)\n// after\nfs.rmSync(statePath, { recursive: true })\nrunSetup({ /* installStatePath now a regular file path */ })","handlingStrategy":"validation","validationCode":"const st = fs.lstatSync(filePath)\nif (!st.isFile() || st.isSymbolicLink()) {\n  throw new Error(`${filePath} must be a regular file`)\n}","typeGuard":"const isRegularFile = (p) => { try { const s = fs.lstatSync(p); return s.isFile() && !s.isSymbolicLink() } catch { return false } }","tryCatchPattern":"try {\n  snapshot(filePath)\n} catch (err) {\n  if (err.message.includes('Refusing to read a non-file at')) {\n    console.error(`${filePath} is not a regular file; fix the path or remove the directory/FIFO`)\n  } else throw err\n}","preventionTips":["lstat the target path before install operations and require a regular file","Avoid placing install-state paths under directories that may be swapped with symlinks","Audit target roots for stray FIFOs/sockets after failed runs"],"tags":["filesystem","safety","symlink"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}