affaan-m/ECC · error · Error
${label} must be a regular, non-symlink file.
Error message
${label} must be a regular, non-symlink file. What it means
readRegularTextFile opened the target with O_RDONLY | O_NOFOLLOW | O_NONBLOCK, but fstat on the resulting descriptor reports a non-regular file. O_NOFOLLOW only rejects a symlink as the final path component; it still succeeds for FIFOs, devices, sockets, and other non-regular inodes. The vault rejects these because memory documents must be plain files that can be size-checked, streamed, and fsynced safely.
Source
Thrown at scripts/lib/memory-vault.js:156
}
return left.dev === right.dev;
}
function readRegularTextFile(filePath, options = {}) {
const label = options.label || 'file';
const maxBytes = options.maxBytes || MAX_DOCUMENT_BYTES;
if (options.trustedRoot) {
assertWithinTrustedRoot(filePath, options.trustedRoot, `read ${label}`);
}
const flags = fs.constants.O_RDONLY
| (fs.constants.O_NOFOLLOW || 0)
| (fs.constants.O_NONBLOCK || 0);
const descriptor = fs.openSync(filePath, flags);
try {
const opened = fs.fstatSync(descriptor, { bigint: true });
if (!opened.isFile()) {
throw new Error(`${label} must be a regular, non-symlink file.`);
}
const after = fs.lstatSync(filePath, { bigint: true });
if (
after.isSymbolicLink()
|| !after.isFile()
|| !sameFileIdentity(after, opened)
) {
throw new Error(`${label} must remain a regular, non-symlink file while it is opened.`);
}
if (options.trustedRoot) {
assertWithinTrustedRoot(filePath, options.trustedRoot, `read ${label}`);
}
if (opened.size > BigInt(maxBytes)) {
throw new Error(`${label} is too large (${opened.size} bytes).`);
}
const chunks = [];
let total = 0;View on GitHub (pinned to 01e15490f0)
Solutions
- Identify the file at the reported path and remove it: rm <path>.
- If legitimate memory content existed there, restore it from a backup as a real file (printf '%s' "$content" > <path> or re-run saveMemory).
- Audit the vault directory for non-regular files: find <root> -type f ! -type r ... or scan with find <root> ! -type d and check each entry.
- Run doctorMemoryVault to surface and quarantine the invalid file.
Example fix
// before: .ecc/memory/project/notes/mem_x.md is a FIFO (mkfifo)
// shell fix:
// rm .ecc/memory/project/notes/mem_x.md
// node -e "require('./scripts/lib/memory-vault').saveMemory({title:'x',body:'...'})"
// after: mem_x.md is a regular file Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
function assertRegularFile(p) {
if (!fs.existsSync(p)) return false;
const st = fs.lstatSync(p);
return st.isFile() && !st.isSymbolicLink();
}
// before reading:
if (!assertRegularFile(filePath)) throw new Error(`${filePath} must be a regular, non-symlink file.`); Type guard
function isRegularNonSymlinkFile(p) {
try { const st = fs.lstatSync(p); return st.isFile() && !st.isSymbolicLink(); }
catch { return false; }
} Try / catch
try { readRegularTextFile(filePath, { trustedRoot: root }); }
catch (error) {
if (/regular, non-symlink file/.test(error.message)) {
console.error('Special or non-regular file in vault:', error.message);
return;
}
throw error;
} Prevention
- Only ever write vault files through saveMemory / writeCreateOnlyTextFile — never mkfifo, mknod, or socket calls inside the vault.
- Audit the vault for non-regular files: find <root> ! -type d ! -type f.
- Run doctorMemoryVault regularly; invalid files show up in the invalidFiles list.
- Treat any non-.md file in the vault as suspicious.
When it happens
Trigger: A memory document path (or the project .gitignore) resolves to a FIFO, character/block device, socket, or other special file. Triggered by readRegularTextFile via readMemoryFiles scanning the vault, ensureProjectScopeIgnored reading .gitignore, or any direct call passing a special file path.
Common situations: A prank or misconfigured script creating a FIFO (mkfifo) named like a memory file; a broken backup that restored a device node; /dev/null or /dev/zero accidentally referenced; an external tool writing a Unix socket inside the vault directory; filesystem corruption exposing a special inode.
Related errors
- 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
- Refusing to read non-file path: ${filePath}
- ${label} must not contain control or bidirectional formattin
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/1df467602b3decf6.
Report an issue: GitHub.