Skip to content

Conversation

@mattietea
Copy link
Contributor

@mattietea mattietea commented Oct 24, 2025

Issue

Knip hangs indefinitely when analyzing workspaces containing circular symlinks in node_modules. This occurs in monorepos where workspace-to-workspace symlinks create circular references

Root Cause

The syncGlob function in src/util/glob.ts uses fast-glob with default options, which includes followSymbolicLinks: true. When encountering circular symlinks, fast-glob enters an infinite traversal loop.

Fix

Added followSymbolicLinks: false to the syncGlob function.

I noticed there is also GLOBAL_IGNORE_PATTERNS - is that the better fix?

Impact

Before:

  • Analysis hangs indefinitely on workspaces with circular symlinks

After:

  • Correctly finds files without traversing symlinks


const syncGlob = ({ cwd, patterns }: { cwd?: string; patterns: string | string[] }) => fg.sync(patterns, { cwd });
const syncGlob = ({ cwd, patterns }: { cwd?: string; patterns: string | string[] }) =>
fg.sync(patterns, { cwd, followSymbolicLinks: false });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps GLOBAL_IGNORE_PATTERNS is the better fix?

@mattietea mattietea marked this pull request as ready for review October 24, 2025 15:49
@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 25, 2025

Open in StackBlitz

npm i https://pkg.pr.new/knip@1319

commit: 26c5eac

@webpro
Copy link
Member

webpro commented Oct 25, 2025

Thanks for the PR!

I do wonder if this actually fixes the issue you described. The fixture currently doesn't seem to fail without this change. So could you please set up something that fails/hangs first so that we have something to work with?

@mattietea
Copy link
Contributor Author

Thanks for the PR!

I do wonder if this actually fixes the issue you described. The fixture currently doesn't seem to fail without this change. So could you please set up something that fails/hangs first so that we have something to work with?

Hi Lars,

Thanks for taking a look at this! Hmm, I seem to be getting the failure.

I thought there might be issues with symlinks and GitHub, so I tried deleting the branch locally and checking it out again but that seems to have worked. Let me know if you have any other ideas! I'm running the test with

bun test ./packages/knip/test/workspaces-circular-symlinks.test.ts

Without Changes

const syncGlob = ({ cwd, patterns }: { cwd?: string; patterns: string | string[] }) =>
  fg.sync(patterns, { cwd });
bun test ./packages/knip/test/workspaces-circular-symlinks.test.ts
bun test v1.2.15 (df017990)

packages/knip/test/workspaces-circular-symlinks.test.ts:
11 | test('syncGlob should not traverse circular symlinks', () => {
12 |   const libACwd = resolve('fixtures/workspaces-circular-symlinks/packages/lib-a');
13 |   const files = _syncGlob({ patterns: ['./**/*.ts'], cwd: libACwd });
14 |
15 |   const expected = 1;
16 |   assert.equal(files.length, expected, `Expected ${expected} file but found ${files.length}`);
              ^
AssertionError: Expected 1 file but found 32

32 !== 1

 generatedMessage: false,
     actual: 32,
   expected: 1,
   operator: "strictEqual",
       code: "ERR_ASSERTION"

      at /Users/mattietea/Desktop/knip/packages/knip/test/workspaces-circular-symlinks.test.ts:16:10
✗ syncGlob should not traverse circular symlinks [8.60ms]

 0 pass
 1 fail
Ran 1 tests across 1 files. [57.00ms]

We can see in this test _globSync is returning the same path 32 times

Truncated return value of _globSync
[
  "index.ts", "node_modules/@fixtures/lib-b/index.ts", "node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/index.ts",
  "node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/index.ts",
  "node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/index.ts",
  "node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/index.ts",
  "node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/index.ts",
  "node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/index.ts",
  "node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/node_modules/@fixtures/lib-b/node_modules/@fixtures/lib-a/index.ts",
  ...
]

With Changes

const syncGlob = ({ cwd, patterns }: { cwd?: string; patterns: string | string[] }) =>
  fg.sync(patterns, { cwd, followSymbolicLinks: false });
bun test ./packages/knip/test/workspaces-circular-symlinks.test.ts
bun test v1.2.15 (df017990)

packages/knip/test/workspaces-circular-symlinks.test.ts:
✓ syncGlob should not traverse circular symlinks [2.71ms]

 1 pass
 0 fail
Ran 1 tests across 1 files. [53.00ms]

@webpro webpro force-pushed the main branch 4 times, most recently from f4844ad to 0faa3b8 Compare December 20, 2025 12:55
@webpro webpro merged commit bffae52 into webpro-nl:main Dec 27, 2025
29 checks passed
@webpro
Copy link
Member

webpro commented Dec 27, 2025

Thanks Matt! Confirmed & merged.

@webpro
Copy link
Member

webpro commented Dec 27, 2025

🚀 This pull request is included in v5.78.0. See Release 5.78.0 for release notes.

Using Knip in a commercial project? Please consider becoming a sponsor.

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.

2 participants