affaan-m/ECC · error · Error

must remain a regular, non-symlink file while it is opened.

Error message

${label} must remain a regular, non-symlink file while it is opened.

What it means

readRegularTextFile re-checks the path with lstat after opening and throws when the file was swapped to a symlink, ceased to be a regular file, or its identity (dev) changed mid-read — a TOCTOU/race guard. The faulting input is the file at `label` being concurrently replaced.

Solutions

  1. Retry the read once no other process is rewriting the file.
  2. Exclude the memory vault from concurrent writers/sync tools during reads.
  3. Investigate what is swapping the file if this recurs — likely a sync or editor tool racing the vault.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at scripts/lib/memory-vault.js:164 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/5e32e46b37708e3b. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/memory-vault.js:164

    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;
    while (total <= maxBytes) {
      const buffer = Buffer.alloc(Math.min(64 * 1024, maxBytes + 1 - total));
      const bytesRead = fs.readSync(descriptor, buffer, 0, buffer.length, null);
      if (bytesRead === 0) break;
      chunks.push(buffer.subarray(0, bytesRead));
      total += bytesRead;
    }
    if (total > maxBytes) {

View on GitHub (pinned to 8321021c54)