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

  1. Identify the file at the reported path and remove it: rm <path>.
  2. If legitimate memory content existed there, restore it from a backup as a real file (printf '%s' "$content" > <path> or re-run saveMemory).
  3. 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.
  4. 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

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


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/1df467602b3decf6. Report an issue: GitHub.