parcel-bundler/parcel · error · FSError

ELOOP

ELOOP

Error message

ELOOP: ${path} is a symlink

What it means

ELOOP raised by `ExtendedMemoryFS.openSync` when `O_NOFOLLOW` is set in the flags and the target path is a symlink. Mirrors POSIX: O_NOFOLLOW forbids following symlinks at open time.

Source

Thrown at packages/dev/repl/src/parcel/ExtendedMemoryFS.js:275

      if (candidate >= FD_MAX) {
        this.nextFD = 1;
        candidate = this.nextFD++;
      }
      if (!this.openFDs.has(candidate)) {
        fd = candidate;
        break;
      }
    }
    if (!fd) {
      throw new FSError('EMFILE', path, 'no available file descriptor');
    }
    return fd;
  }

  openSync(filePath: FilePath, flags: number, mode: number): number {
    flags = parseOpenFlags(flags);
    if (flags & CONSTANTS.O_NOFOLLOW && this.symlinks.has(filePath)) {
      throw new FSError('ELOOP', filePath, 'is a symlink');
    }

    filePath = this._normalizePath(filePath);

    let file = this.files.get(filePath);
    if (flags & CONSTANTS.O_CREAT) {
      if (file) {
        if (flags & CONSTANTS.O_EXCL) {
          throw new FSError('EEXIST', filePath, 'already exists');
        }
      } else {
        file = new File(makeShared(''), mode);
        this.files.set(filePath, file);
      }
    }
    if (!file) {
      throw new FSError('ENOENT', filePath, 'does not exist');
    } else if (flags & CONSTANTS.O_TRUNC) {

View on GitHub (pinned to 59484858a1)

Solutions

  1. If following the link is intended, drop the `O_NOFOLLOW` flag.
  2. If the path must be a regular file, reject or fix the symlink upstream before opening.
  3. Resolve the real path first with `realpathSync` if you want the link target.

Example fix

// before
fs.openSync(symlinkPath, O_RDONLY | O_NOFOLLOW);  // ELOOP
// after
fs.openSync(symlinkPath, O_RDONLY);  // follow allowed
Defensive patterns

Strategy: validation

Validate before calling

function openNoFollowSafe(fs, p, flags) {
  if ((flags & O_NOFOLLOW) && fs.symlinks.has(p)) throw new Error(`${p} is a symlink`);
  return fs.openSync(p, flags);
}

Type guard

function isSymlink(fs, p) { return fs.symlinks.has(p); }

Try / catch

try { return fs.openSync(p, O_RDONLY | O_NOFOLLOW); }
catch (e) { if (e.code === 'ELOOP') return fs.openSync(p, O_RDONLY); throw e; }

Prevention

When it happens

Trigger: Opening a path with the `O_NOFOLLOW` flag when that path is recorded as a symlink in the memory FS; defensive opens against an attacker-controlled path that turned out to be a link.

Common situations: Security-hardened code that uses O_NOFOLLOW to avoid symlink races; tooling that opens files written by a process that created symlinks.

Related errors


AI-assisted analysis of parcel-bundler/parcel@59484858a1 (2026-08-13). Data as JSON: /api/errors/8729017fe754e78f. Report an issue: GitHub.