affaan-m/ECC · error
Refusing to read non-file path
Error message
Refusing to read non-file path: ${filePath} What it means
readFileWithMetadataNoFollow opens files with O_NOFOLLOW and then double-checks with fstat that the opened descriptor is a regular file before reading. If the path resolved to something else (symlink, directory, FIFO, device), the library refuses to read it rather than following or misinterpreting it. This prevents TOCTOU symlink attacks when reading files named by untrusted install state.
Solutions
- Point the operation at a regular file path, not a directory, symlink, or special file.
- If the target is a symlink to a real file, use the real file's path (or replace the symlink with a copy) since O_NOFOLLOW refuses to follow links.
- Re-run the operation if it was a transient race; investigate what replaced the file if it recurs.
- Verify the path with `ls -la` / `file` and ensure it is a plain regular file before retrying.
Example fix
// before: state says destinationPath: ~/.claude/settings.json, but that is a directory
// after
rm -rf ~/.claude/settings.json # remove the directory
echo '{}' > ~/.claude/settings.json # real file, then retry install Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
function assertRegularFileNoSymlink(p) {
const st = fs.lstatSync(p); // lstat: does not follow symlinks
if (!st.isFile()) throw new Error(`Not a regular file: ${p}`);
} Type guard
function isPlainRegularFile(p) {
try { const st = fs.lstatSync(p); return st.isFile() && !st.isSymbolicLink(); } catch { return false; }
} Try / catch
try {
await install(operations);
} catch (err) {
if (/Refusing to read non-file path/.test(err.message)) {
console.error(`Check ${err.message.split(': ')[1]} — replace symlink/dir with a real file.`);
} else throw err;
} Prevention
- Ensure paths in install-state point at regular files, never directories or symlinks.
- Resolve symlinks to their real targets before recording paths in state.
- Avoid concurrent processes that replace managed files with links during installs.
When it happens
Trigger: Calling the read helper with a path that is not a regular file at open time: a directory path, a symlink (O_NOFOLLOW makes open fail or the check fails), a FIFO/socket/device node, or a path swapped between the existsSync check and the read.
Common situations: install-state recording a destinationPath that is actually a directory; users symlinking managed files elsewhere; a race where another process replaces the file with a symlink or special file while install runs.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- gate.variant_invalid
- Invalid ECC repo root: missing package.json at
- Nasiko executable must be a regular file, not a symlink.
- Nasiko install directory must be a real directory, not a…
- output artifact must be a regular file
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/78a9de293ba2c0ad.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install-lifecycle.js:520
);
fs.ftruncateSync(fileDescriptor, 0);
fs.writeFileSync(fileDescriptor, content);
if (mode !== undefined) {
fs.fchmodSync(fileDescriptor, mode);
}
} finally {
fs.closeSync(fileDescriptor);
}
}
function readFileWithMetadataNoFollow(filePath, encoding) {
const flags = fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0);
const fileDescriptor = fs.openSync(filePath, flags);
try {
const stat = fs.fstatSync(fileDescriptor);
if (!stat.isFile()) {
throw new Error(`Refusing to read non-file path: ${filePath}`);
}
return {
content: fs.readFileSync(fileDescriptor, encoding),
mode: stat.mode,
};
} finally {
fs.closeSync(fileDescriptor);
}
}
function readFileNoFollow(filePath, encoding) {
return readFileWithMetadataNoFollow(filePath, encoding).content;
}
function readJsonNoFollow(filePath) {
return JSON.parse(readFileNoFollow(filePath, 'utf8'));
}
View on GitHub (pinned to 8321021c54)