Skip to content

fix(skill): pre-scan circular/broken symlinks to prevent ENAMETOOLONG crash#27639

Open
litown wants to merge 2 commits into
anomalyco:devfrom
litown:fix/skill-symlink-crash
Open

fix(skill): pre-scan circular/broken symlinks to prevent ENAMETOOLONG crash#27639
litown wants to merge 2 commits into
anomalyco:devfrom
litown:fix/skill-symlink-crash

Conversation

@litown

@litown litown commented May 15, 2026

Copy link
Copy Markdown

Issue for this PR

Fixes #27638

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

On Bun runtime, opencode crashes with ENAMETOOLONG during skill scanning when external skill directories (~/.claude/skills/, ~/.agents/skills/) contain circular or broken symlinks. The glob library with follow: true enters infinite recursion on circular symlinks, and Bun's readdir throws ENAMETOOLONG when path strings exceed PATH_MAX. Node.js handles this gracefully, but Bun does not.

This PR adds a pre-scan step (findBrokenSymlinks) that recursively detects circular symlinks (via readlink + path resolution + realpath visited set) and broken symlinks (via stat ENOENT/ELOOP), then passes detected paths as glob ignore patterns. A subdirs option limits the pre-scan to only directories glob would traverse (e.g., only skills/ under ~/.claude/), reducing pre-scan time from ~1.3s to ~19ms.

Related work: PR #18670 addresses the same root cause with a more fundamental approach (replacing glob with a custom cycle-safe walker). However, #18670 targets an older codebase (pre-Effect-TS, zod-based) and likely needs significant rework to merge. This PR is adapted to the current Effect-TS codebase and provides an immediate fix.

Changes:

  • packages/core/src/util/glob.ts: Expose ignore option in Glob.Options
  • packages/opencode/src/skill/index.ts: Add findBrokenSymlinks() with recursive symlink detection, SCAN_IGNORE_PATTERNS for standard deep directories, and pass results as glob ignore in all scan call sites

How did you verify your code works?

  • Verified on ~/.claude/skills/ with 5 broken symlinks and 3 circular symlinks
  • Pre-scan with subdirs filter: 19ms (vs 1283ms without filter)
  • Glob scan with filtered ignore: 439ms, 1819 files found, no crash
  • TypeScript compilation passes (tsc --noEmit)
  • Tested with Node.js runtime: glob completes in 73ms without crash
  • Tested with Bun runtime: previously crashed with ENAMETOOLONG, now completes successfully

Screenshots / recordings

N/A

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

🤖 Generated with Claude Code

@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label May 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Found a potentially related PR:

PR #18670: fix(opencode): avoid symlink-loop recursion during skill discovery

This PR appears to address a similar issue - preventing symlink loops/recursion during skill discovery. While it's a historical PR, it's worth reviewing to ensure the current fix (PR #27639) doesn't duplicate or conflict with the approach taken there.

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label May 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

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.

fix(skill): circular symlinks in external skill dirs cause ENAMETOOLONG crash on Bun runtime

1 participant