Skip to content

fix(fix): skip unreadable dirs in manifest discovery (EACCES scandir)#1347

Open
Martin Torp (mtorp) wants to merge 2 commits into
v1.xfrom
martin/fix-manifest-discovery-eacces
Open

fix(fix): skip unreadable dirs in manifest discovery (EACCES scandir)#1347
Martin Torp (mtorp) wants to merge 2 commits into
v1.xfrom
martin/fix-manifest-discovery-eacces

Conversation

@mtorp
Copy link
Copy Markdown
Contributor

@mtorp Martin Torp (mtorp) commented May 29, 2026

Problem

socket fix (and socket scan create / socket scan reach) crash with

✖  Unexpected error:  EACCES: permission denied, scandir '.../data/postgres/pgdata'

when the project contains a directory the running user cannot enter — e.g. a postgres pgdata data directory owned by another uid (mode drwx------), or an unreadable Docker volume mount.

#1339 added --exclude-paths (and socket.yml projectIgnorePaths already existed) specifically to skip such directories, but neither worked — the command crashed identically even with the path excluded.

Root cause

globWithGitIgnore (src/utils/glob.mts) runs two fast-glob walks:

  1. a .gitignore discovery walk (fastGlob.globStream(['**/.gitignore'], …)) to build the ignore set, then
  2. the main package-file walk.

--exclude-paths (additionalIgnores) and projectIgnorePaths were only ever applied to walk 2. Walk 1 runs first with just the default ignore list, so it scandirs straight into the unreadable directory and throws before any user exclusion can take effect. That's why excluding the path didn't help.

Verified empirically with a reproduction (chmod 000 dir): the discovery walk throws EACCES; the main walk with the exclude succeeds (the #1339 fix is correct, just unreachable); both walks with suppressErrors succeed.

Fix

In globWithGitIgnore:

  1. Thread projectIgnorePaths + --exclude-paths into the .gitignore discovery walk (negations dropped — for a discovery walk they could only re-include a subtree) so explicit exclusions govern the entire discovery, as the flag documents.
  2. suppressErrors: true on both walks — a directory the user cannot read cannot contain manifests they could scan anyway, so skip it instead of aborting the whole run. This makes the tool robust without requiring any flag.

Test

New regression test in src/utils/glob.test.mts reproduces the scenario with a real chmod 000 directory (skipped under root, where perm checks are bypassed, and on Windows). It fails with the exact EACCES … scandir before the fix and passes after.

Release

Cuts 1.1.112 (package.json + CHANGELOG.md).


Note

Medium Risk
Changes shared file-walking used by fix and scan; suppressErrors could mask rare filesystem errors, but scope is limited to skipping unreadable paths users could not scan anyway.

Overview
v1.1.112 fixes manifest discovery so socket fix and socket scan create no longer die on EACCES: permission denied, scandir when the repo contains directories the current user cannot read (e.g. Postgres pgdata, Docker volume mounts).

globWithGitIgnore runs a separate walk to find **/.gitignore before the main file walk. --exclude-paths and socket.yml projectIgnorePaths were only applied to the main walk, so the discovery walk could still enter an unreadable tree and abort before exclusions took effect. The PR applies the same ignore patterns to the discovery walk (dropping negated ! patterns there) and sets suppressErrors: true on both walks so unreadable directories are skipped instead of failing the whole command.

A POSIX integration test uses a real chmod 000 directory (skipped on Windows and as root).

Reviewed by Cursor Bugbot for commit 21c7770. Configure here.

…unreadable dirs

socket fix and socket scan create walk the project tree for .gitignore
files to build their ignore set before collecting manifests. That walk ran
through fast-glob with only the default ignore list — neither --exclude-paths
nor socket.yml projectIgnorePaths were applied to it. A directory the running
user cannot enter (a postgres pgdata dir owned by another uid, an unreadable
Docker volume mount, etc.) therefore aborted the whole command with

  EACCES: permission denied, scandir '.../data/postgres/pgdata'

during this discovery walk, before any exclusion could take effect — which is
why excluding the path did not help.

Thread projectIgnorePaths and --exclude-paths into the .gitignore discovery
walk so explicit exclusions govern the entire discovery, and set
suppressErrors on both that walk and the main package walk so an unreadable
directory is skipped rather than crashing the run. A directory the user
cannot read cannot contain manifests they could scan anyway.

Add a regression test that reproduces the crash with a real chmod 000
directory (skipped under root and on Windows), and cut 1.1.112.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 21c7770. Configure here.

Comment thread src/utils/glob.mts
…rridden

suppressErrors is the EACCES backstop for manifest discovery, not a tunable.
Placing it before ...additionalOptions let a caller's options bag flip it back
to false and re-introduce the crash. Move it after the spread so the safety
invariant always holds. Flagged in review; no current caller passes
suppressErrors, so behavior is unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants