Skip to content

fix: skip symlink cycles during pack instead of failing with ELOOP#293

Open
gagan-53 wants to merge 1 commit into
modelcontextprotocol:mainfrom
gagan-53:fix/292-symlink-cycle-pack
Open

fix: skip symlink cycles during pack instead of failing with ELOOP#293
gagan-53 wants to merge 1 commit into
modelcontextprotocol:mainfrom
gagan-53:fix/292-symlink-cycle-pack

Conversation

@gagan-53

Copy link
Copy Markdown

Fixes #292.

Problem

When an extension directory contains a symlink cycle (e.g. ln -s . selfloop), mcpb pack aborts with a raw OS error and produces no archive:

ERROR: Archive error: ELOOP: too many symbolic links encountered, stat '.../demo-ext/selfloop/selfloop/.../selfloop'

getAllFiles and getAllFilesWithCount in src/node/files.ts walk the tree with statSync (which follows symlinks) and recurse into anything reported as a directory, with no tracking of already-visited paths, so a cyclic link descends until the OS raises ELOOP.

Fix

Track the real paths (realpathSync) of the directories on the current traversal path. Before recursing into a directory, resolve its real path; if it is already on the traversal path, emit a warning naming the offending path and skip it:

Warning: Symlink cycle detected, skipping: selfloop

Packing then continues and succeeds. Behavior for everything else is unchanged:

  • Non-cyclic symlinks (files and directories) are still followed and their targets packed, as before — important for layouts that rely on symlinked directories.
  • Only the traversal path is tracked (entries are removed after recursion returns), so two independent links to the same directory are still both packed, as before.
  • The new visitedRealPaths parameter is optional and trailing on both exported functions, so the public API is backward compatible.

Testing

  • New test/symlink-cycle.test.ts: self-referential symlink, mutually recursive symlinks (A→B, B→A), a non-cyclic symlink still being followed, and coverage of both getAllFiles and getAllFilesWithCount. The cycle tests fail with ELOOP without the fix. Symlinks are created with type junction (works unprivileged on Windows; ignored elsewhere) and the tests no-op if the environment cannot create symlinks.
  • yarn test: 129 passed, 9 suites.
  • yarn lint: clean.
  • Manual repro from the issue: before — ERROR: Archive error: ELOOP: ..., exit 1, no archive; after — warning + successful pack, archive written, exit 0.

getAllFiles and getAllFilesWithCount follow symlinks via statSync and
recurse into anything reported as a directory, so a symlink cycle (e.g.
a self-referential link) descends until the OS raises ELOOP and pack
aborts with a cryptic error.

Track the real paths of directories on the current traversal path and,
when recursing would revisit one, warn with the offending path and skip
it. Non-cyclic symlinks are still followed and packed as before.

Fixes modelcontextprotocol#292

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mcpb pack fails with a cryptic ELOOP error when the extension directory contains a symlink cycle

1 participant