affaan-m/ECC · error · Error
Refusing to read non-file path: ${filePath}
Error message
Refusing to read non-file path: ${filePath} What it means
readFileWithMetadataNoFollow opens the path with O_RDONLY | O_NOFOLLOW (so the final component is not followed) and then fstat's the descriptor. If the descriptor does not point at a regular file (S_ISREG false — e.g. it is a directory, pipe, socket, device, or symlink target), reading is refused. This prevents the installer from reading attacker-controlled non-file entries as if they were file contents.
Source
Thrown at scripts/lib/install-lifecycle.js:414
);
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 01e15490f0)
Solutions
- Identify the path from the error message and run `ls -la <path>` to confirm its type.
- If it is a directory or symlink, remove or rename it: `rm -rf <path>` or `mv <path> <path>.bak`.
- Re-run the install/repair operation so a real file is written.
- Audit how the non-file entry was created (failed prior install, manual mkdir, sync tool) to prevent recurrence.
Example fix
// before: ~/.claude/install-state.json is a directory // after: rm -rf ~/.claude/install-state.json ./install.sh --target claude
Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
function assertRegularFile(filePath) {
const st = fs.lstatSync(filePath);
if (st.isSymbolicLink()) throw new Error('path is a symlink');
if (!st.isFile()) throw new Error('path is not a regular file');
}
// before reading via the library helpers
assertRegularFile(stateFilePath); Type guard
function isReadableRegularFile(filePath) {
try {
return fs.lstatSync(filePath).isFile() && !fs.lstatSync(filePath).isSymbolicLink();
} catch {
return false;
}
} Try / catch
try {
content = readJsonNoFollow(path);
} catch (err) {
if (err.message.startsWith('Refusing to read non-file path')) {
// remove or rename the offending entry, then regenerate state
} else throw err;
} Prevention
- Treat install-state and recorded destinations as read-only after install.
- Do not replace installed files with directories or symlinks manually.
- Run a sanity `ls -la` on the install root before invoking repair/uninstall.
- Keep backups of install-state so a corrupted entry can be restored.
When it happens
Trigger: Any internal call to readFileNoFollow / readFileWithMetadataNoFollow / readJsonNoFollow / copyContainedFile when the resolved path is a directory, a symlink (blocked by O_NOFOLLOW at open), a FIFO, or a special file. Common during repair when an install-state points at a destination that has since become a directory.
Common situations: A destination recorded in the install-state file was replaced by a directory or a symlink; repair then tries to read it as a file. A previous failed install left a directory where a JSON file was expected (e.g. ~/.claude/install-state.json is a directory).
Related errors
- Refusing to ${action}: destination parent is not a trusted d
- TypeScript compiler not found. Install root dev dependencies
- ${source} is missing the expected catalog marker
- ${EXECUTABLE_OVERRIDE} does not point to a readable local It
- Missing source file for repair: ${sourcePath || operation.so
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/78a9de293ba2c0ad.
Report an issue: GitHub.