You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(test): stop Vitest failing green unit shards with EISDIR on directory stack frames - #34335
Unit shards sometimes fail with every test passing:
Test Files 2618 passed | 43 skipped (2662)
Errors 1 error
⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯
Error: EISDIR: illegal operation on a directory, read
❯ getSourceMap node_modules/.pnpm/vitest@5.0.5…/dist/chunks/cli-api.De07GUeT.js:11849:21
❯ parseErrorStacktrace …
When Vitest annotates a task error, it looks up a source map for each stack frame. The check is existsSync(file) followed by readFileSync(file). If a frame's path is a directory, existsSync returns true, readFileSync throws EISDIR, and Vitest reports it as an unhandled error. The run exits 1, and the original test result is swallowed.
This PR extends our existing vitest@5.0.5 patch: the source-map lookup now reads the file only when statSync(...).isFile(). 5.0.5 is the latest Vitest release, so there's no upstream fix to adopt. The lockfile change is only the patch hash.
Impact
Across the 100 most recent failed pr.yml runs (~4.5 h on 2026-10-27), 5 of 32 failed unit-shard jobs were pure EISDIR flakes with zero failing tests. That's ~25 spurious red PR runs/day, each needing a shard rerun plus a re-wait on verify. One hit #34324 today.
Verification
Locally, a throwaway test throwing an error whose stack names a directory:
old patch: Unhandled Errors … EISDIR, Test Files (1) with no result, Errors 1 error
this patch: Test Files 1 failed (1), the real test failure, with no unhandled error
CI on this PR runs the full unit suite on the patched Vitest.
…tory stack frames
Vitest's reporter checks existsSync(file) and then reads the file to find a
source map. When an error stack names a directory, readFileSync throws EISDIR
as an unhandled error, so the shard fails with every test passing. Read only
regular files.
The Vitest patch now checks that the source-map fallback path is a file before it reads and extracts a source map. The patch metadata also records a new blob hash.
Priority: Normal
Merge Risk: Low · up to fd762
A narrow stack-path edge case can still produce an unhandled Vitest reporter error; catch stat failures before merging.
Pre-merge checks: 4 · 1
Failed checks: 1 warning
Check name
Status
Explanation
Resolution
Description check
The description covers most of the template. It has a clear What section with the failure output, the mechanism (existsSync then readFileSync on a directory throws EISDIR), and the fix (statSync(...).…
Add an ELI5 section in plain language. Link an actual issue in the Linked Issue section (replace the blank Fixes #). Add Visual Proof as N/A with a short reason, since this is a test-tooling change. In Testing, state which platforms were te…
Passed checks: 4 passed
Check name
Status
Explanation
Title check
The title names the change (a fix for Vitest failing unit shards with EISDIR when a stack frame is a directory) and matches the PR objective of reading source-map paths only when they are regular file…
Docstring Coverage
No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within survi…
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Full details: Title check
Explanation
The title names the change (a fix for Vitest failing unit shards with EISDIR when a stack frame is a directory) and matches the PR objective of reading source-map paths only when they are regular files. It is specific and scoped to the change, and it is not vague. It is 86 characters, slightly long, but it is still a single clear sentence that a teammate scanning history would understand.
Full details: Docstring Coverage
Explanation
No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 0 files. (1 skipped: 1 unsupported.)
Full details: Description check
Explanation
The description covers most of the template. It has a clear What section with the failure output, the mechanism (existsSync then readFileSync on a directory throws EISDIR), and the fix (statSync(...).isFile()). It has a Why section that gives impact numbers and notes that no upstream fix exists, and a Verification section with before and after local results. It also has the LoC summary and a Testing note. Several template sections are missing or incomplete: ELI5 is absent, the Linked Issue section has no issue (Fixes # is blank, and the template requires an actual linked issue for outside contributors), the Visual Proof section is absent (a N/A with reason would be needed if no UI change), the Testing checklist boxes are unchecked and the platforms actually tested are not stated, the AI Disclosure section is missing, the Agent skill upstream boundary checkbox is not addressed, and the Notes and Checklist items are not confirmed. The Why section also does not discuss alternatives considered.
Resolution
Add an ELI5 section in plain language. Link an actual issue in the Linked Issue section (replace the blank Fixes #). Add Visual Proof as N/A with a short reason, since this is a test-tooling change. In Testing, state which platforms were tested (macOS, Linux, Windows, or SSH), and tick the boxes that apply. Add a note on alternatives considered in the Why section, such as upstream fix, skipping the shard, or changing the test. Fill in or mark N/A the AI Disclosure section if applicable. Address the Agent skill upstream boundary checkbox. Confirm the Checklist items, including pnpm lint, typecheck, test, and build status.
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Review info
Run configuration
Source mode: Auto
Repository: Unavailable
Branch: Unavailable
File: No configuration file from this repository applied
Commit: Unavailable
Layers: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: f57c24dc-ad63-49c4-a2ad-d3be359731cf
Commits
Reviewing files that changed from the base of the PR and between b1d1200 and fd7624c.
Files ignored due to path filters: 1
pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
Files selected for processing: 1
config/patches/vitest@5.0.5.patch
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.
<source_evidence>
<source kind="find_in_page">
<title>File system | Node.js v20.19.0 Documentation</title>
<location>https://nodejs.org/download/release/v20.19.0/docs/api/fs.html</location>
<excerpt>citeturn2view2 [wordlim: 200] Crawled: today; Content type: text/html; Source: find({"ref_id":"turn1view0","pattern":"If the file does not exist"}); Total lines: 5072
L1491: console.log(`${file} ${err ? 'does not exist' : 'exists'}`);
L1492: });
L1493:
L1494: // Check if the file is readable.
L1495: access(file, constants.R_OK, (err) => {
L1496: console.log(`${file} ${err ? 'is not readable' : 'is readable'}`);
L1497: });
L1498:
L1499: // Check if the file is writable.
L1500: access(file, constants.W_OK, (err) => {
L1501: console.log(`${file} ${err ? 'is not writable' : 'is writable'}`);
L1502: });
L1503:
L1504: // Check if the file is readable and writable.
L1508: Do not use `fs.access()` to check for the accessibility of a file before calling `fs.open()`, `fs.readFile()`, or `fs.writeFile()`. Doing so introduces a race condition, since other processes may change the file's state between the two calls. Instead, user code should open/read/write the file directly and handle the error raised if the file is not accessible.
L1553: read (NOT RECOMMENDED)
L1554:
L1555: `import { access, open, close } from 'node:fs';
L1556: access('myfile', (err) => {
L1557: if (err) {
L1558: if (err.code === 'ENOENT') {
L1559: console.error('myfile does not exist');
L1560: return;
L1561: }
L1562:
L1563: throw err;
L1564: }
L1565:
L1566: open('myfile', 'r', (err, fd) => {
L1567: if (err) throw err;
L1568:
L1569: try {
L1570: readMyData(fd);
L1571: } finally {
L1572: close(fd, (err) => {
L1573: if (err) throw err;
L1574: });
L1575: }
L1576: });
L1578: read (RECOMMENDED)
L1579:
L1580: `import { open, close } from 'node:fs';
L1581:
L1582: open('myfile', 'r', (err, fd) => {
L1583: if (err) {
L1584: if (err.code === 'ENOENT') {
L1585: console.error('myfile does not exist');
L1586: return;
L1587: }
L1588:
L1589: throw err;
L1590: }
L1591:
L1592: try {
L1593: readMyData(fd);
L1594: } finally {
L1595: close(fd, (err) => {
L1596: if (err) throw err;
L1597: });
L1598: }
L1599: });` [Button: copy]
L1600: The "not recommended" examples above check for accessibility and then use the file; the "recommended" examples are better because they use the file directly and handle the error, if any.
L1601:
L1602: In general, check for the accessibility of a file only if the file will not be used directly, for example when its accessibility is a signal from another process.
L1603: On Windows, access-control policies (ACLs) on a directory may limit access to a file or directory. The `fs.access()` function, however, does not check the ACL and therefore may report that a path is accessible even if the ACL restricts the user from reading or writing to it.
L1939: Using `fs.exists()` to check for the existence of a file before calling `fs.open()`, `fs.readFile()`, or `fs.writeFile()` is not recommended. Doing so introduces a race condition, since other processes may change the file's state between the two calls. Instead, user code should open/read/write the file directly and handle the error raised if the file does not exist.
L3824: #### `fs.statSync(path[, options])`【163†#】
L3825:
L3826: History
L3827: Version | Changes
L3828: --- | ---
L3829: v15.3.0, v14.17.0 | Accepts a `throwIfNoEntry` option to specify whether an exception should be thrown if the entry does not exist.
L3830: v10.5.0 | Accepts an additional `options` object to specify whether the numeric values returned should be bigint.
L3831: v7.6.0 | The `…[truncated]</excerpt>
</source>
<source kind="open_page">
<title>File system | Node.js v20.19.0 Documentation</title>
<location>https://nodejs.org/download/release/v20.19.0/docs/api/fs.html</location>
<excerpt>citeturn1view0 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({"ref_id":"https://nodejs.org/download/release/v20.19.0/docs/api/fs.html","lineno":null}); Total lines: 5072
L0: 【0†Skip to content】
L1: # Node.js v20.19.0 documentation
L2:
L3: * Node.js v20.19.0
L4: * 【1†Table of contents】
L5: * 【2†File system】
L6: * 【3†Promise example】
L7: * 【4†Callback example】
L8: * 【5†Synchronous example】
L9: * 【6†Promises API】
L10: * 【7†Class: `FileHandle`】
L11: * 【8†Event: `'close'`】
L12: * 【9†`filehandle.appendFile(data[, options])`】
L13: * 【10†`filehandle.chmod(mode)`】
L14: * 【11†`filehandle.chown(uid, gid)`】
L15: * 【12†`filehandle.close()`】
L16: * 【13†`filehandle.createReadStream([options])`】
L17: * 【14†`filehandle.createWriteStream([options])`】
L18: * 【15†`filehandle.datasync()`】
L19: * 【16†`filehandle.fd`】
L20: * 【17†`filehandle.read(buffer, offset, length, position)`】
L21: * 【18†`filehandle.read([options])`】
L22: * 【19†`filehandle.read(buffer[, options])`】
L23: * 【20†`filehandle.readableWebStream([options])`】
L24: * 【21†`filehandle.readFile(options)`】
L25: * 【22†`filehandle.readLines([options])`】
L26: * 【23†`filehandle.readv(buffers[, position])`】
L27: * 【24†`filehandle.stat([options])`】
L28: * 【25†`filehandle.sync()`】
L29: * 【26†`filehandle.truncate(len)`】
L30: * 【27†`filehandle.utimes(atime, mtime)`】
L31: * 【28†`filehandle.write(buffer, offset[, length[, position]])`】
L32: * 【29†`filehandle.write(buffer[, options])`】
L33: * 【30†`filehandle.write(string[, position[, encoding]])`】
L34: * 【31†`filehandle.writeFile(data, options)`】
L35: * 【32†`filehandle.writev(buffers[, position])`】
L36: * 【33†`filehandle[Symbol.asyncDispose]()`】
L37: * 【34†`fsPromises.access(path[, mode])`】
L38: * 【35†`fsPromises.appendFile(path, data[, options])`】
L39: * 【36†`fsPromises.chmod(path, mode)`】
L40: * 【37†`fsPromises.chown(path, uid, gid)`】
L41: * 【38†`fsPromises.copyFile(src, dest[, mode])`】
L42: * 【39†`fsPromises.cp(src, dest[, options])`】
L43: * 【40†`fsPromises.lchmod(path, mode)`】
L44: * 【41†`fsPromises.lchown(path, uid, gid)`】
L45: * 【42†`fsPromises.lutimes(path, atime, mtime)`】
L46: * 【43†`fsPromises.link(existingPath, newPath)`】
L47: * 【44†`fsPromises.lstat(path[, options])`】
L48: * 【45†`fsPromises.mkdir(path[, options])`】
L49: * 【46†`fsPromises.mkdtemp(prefix[, options])`】
L50: * 【47†`fsPromises.open(path, flags[, mode])`】
L51: * 【48†`fsPromises.opendir(path[, options])`】
L52: * 【49†`fsPromises.readdir(path[, options])`】
L53: * 【50†`fsPromises.readFile(path[, options])`】
L54: * 【51†`fsPromises.readlink(path[, options])`】
L55: * 【52†`fsPromises.realpath(path[, options])`】
L56: * 【53†`fsPromises.rename(oldPath, newPath)`】
L57: * 【54†`fsPromises.rmdir(path[, options])`】
L58: * 【55†`fsPromises.rm(path[, options])`】
L59: * 【56†`fsPromises.stat(path[, options])`】
L60: * 【57†`fsPromises.statfs(path[, options])`】
L61: * 【58†`fsPromises.symlink(target, path[, type])`】
L62: * 【59†`fsPromises.truncate(path[, len])`】
L63: * 【60†`fsPromises.unlink(path)`】
L64: * 【61†`fsPromises.utimes(path, atime, mtime)`】
L65: * 【62†`fsPromises.watch(filename[, options])`】
L66: * 【63†`fsPromises.writeFile(file, data[, options])`】
L67: * 【64†`fsPromises.constants`】
L68: * 【65†Callback API】
L69: * 【66†`fs.access(path[, mode], callback)`】
L70: * 【67†`fs.appendFile(path, data[, options], callback)`】
L71: * 【68†`fs.chmod(path, mode, callbac…[truncated]</excerpt>
</source>
<source kind="open_page">
<title>node/lib/fs.js at v20.19.0 · nodejs/node · GitHub</title>
<location>https://github.com/nodejs/node/blob/v20.19.0/lib/fs.js</location>
<excerpt>citeturn1view1 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({"ref_id":"https://github.com/nodejs/node/blob/v20.19.0/lib/fs.js","lineno":null}); Total lines: 3873
L0: 【0†Skip to content】
L1: ## Navigation Menu
L2:
L3: 【1†Sign in】
L4:
L5: Appearance settings
L6:
L7: * Platform
L8:
L9: * AI CODE CREATION
L10: * 【2†GitHub CopilotWrite better code with AI】
L11: * 【3†GitHub Copilot appDirect agents from issue to merge】
L12: * 【4†MCP RegistryIntegrate external tools】
L13:
L14: * DEVELOPER WORKFLOWS
L15: * 【5†ActionsAutomate any workflow】
L16: * 【6†CodespacesInstant dev environments】
L17: * 【7†IssuesPlan and track work】
L18: * 【8†Code ReviewManage code changes】
L19: * 【9†Code QualityEnforce quality at merge】
L20: * APPLICATION SECURITY
L21: * 【10†GitHub Advanced SecurityFind and fix vulnerabilities】
L22: * 【11†Code securitySecure your code as you build】
L23: * 【12†Secret protectionStop leaks before they start】
L24:
L25: * EXPLORE
L26: * 【13†Why GitHub】
L27: * 【14†Documentation†docs.github.com】
L28: * 【15†Blog†github.blog】
L29: * 【16†Changelog†github.blog】
L30: * 【17†Marketplace】
L31:
L32: 【18†View all features】
L33:
L34: * Solutions
L35: * BY COMPANY SIZE
L36: * 【19†Enterprises】
L37: * 【20†Small and medium teams】
L38: * 【21†Startups】
L39: * 【22†Nonprofits】
L40:
L41: * BY USE CASE
L42: * 【23†App Modernization】
L43: * 【24†DevSecOps】
L44: * 【25†DevOps】
L45: * 【26†CI/CD】
L46: * 【27†View all use cases】
L47:
L48: * BY INDUSTRY
L49: * 【28†Healthcare】
L50: * 【29†Financial services】
L51: * 【30†Manufacturing】
L52: * 【31†Government】
L53: * 【32†See all industries】
L54:
L55: 【33†View all solutions】
L56:
L57: * Resources
L58: * EXPLORE BY TOPIC
L59: * 【34†AI】
L60: * 【35†Software Development】
L61: * 【36†DevOps】
L62: * 【37†Security】
L63: * 【38†View all topics】
L64:
L65: * EXPLORE BY TYPE
L66: * 【39†Customer stories】
L67: * 【40†Events & webinars】
L68: * 【41†Ebooks & reports】
L69: * 【42†Business insights】
L70: * 【43†GitHub Skills†skills.github.com】
L71: * SUPPORT & SERVICES
L72: * 【14†Documentation†docs.github.com】
L73: * 【44†Customer support†support.github.com】
L74: * 【45†Community forum】
L75: * 【46†Trust center】
L76: * 【47†Partners】
L77:
L78: 【48†View all resources】
L79:
L80: * Open Source
L81:
L82: * COMMUNITY
L83: * 【49†GitHub SponsorsFund open source developers】
L84: * PROGRAMS
L85: * 【50†Security Lab†securitylab.github.com】
L86: * 【51†Maintainer Community†maintainers.github.com】
L87: * 【52†GitHub Stars†stars.github.com】
L88: * 【53†Archive Program†archiveprogram.github.com】
L89:
L90: * REPOSITORIES
L91: * 【54†Topics】
L92: * 【55†Trending】
L93: * 【56†Collections】
L94:
L95: * Enterprise
L96:
L97: * ENTERPRISE SOLUTIONS
L98: * 【19†Enterprise platformAI-powered developer platform】
L99: * AVAILABLE ADD-ONS
L100: * 【10†GitHub Advanced SecurityEnterprise-grade security features】
L101: * 【57†Copilot for BusinessEnterprise-grade AI features】
L102: * 【58†Premium SupportEnterprise-grade 24/7 support】
L103:
L104: * 【59†Pricing】
L105:
L106: Search`/`
L107:
L108: 【1†Sign in】
L109:
L110: 【60†Sign up】
L111:
L112: Appearance settings
L113: You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. Dismiss alert
L114:
L115: 1. 【61†nodejs】
L116: 2. 【62†node】
L117: ## Repository navigation
L118:
L119: * * 【62†Code】
L120: * 【63†Issues629 (629)】
L121: * 【64†Pull requests662 (662)】
L…[truncated]</excerpt>
</source>
<source kind="search">
<title>node/doc/api/fs.md at main · nodejs/node · GitHub</title>
<location>https://github.com/nodejs/node/blob/main/doc/api/fs.md?plain=1</location>
<excerpt>citeturn0search0 [wordlim: 200] Crawled: last month; See the POSIX rename(2) documentation for more details. ### `fs.rmdirSync(path[, options])` <!-- YAML added: v0.1.21 changes: - version: v25.0.0 pr-url: https://github.com/nodejs/node/pull/58616 description: Remove `recursive` option. - version: v16.0.0 pr-url: https://github.com/nodejs/node/pull/37216 description: "Using `fs.rmdirSync(path, { recursive: true })` on a `path` that is a file is no longer permitted and results in an `ENOENT` error on Windows and an `ENOTDIR` error on POSIX." - version: v16.0.0 pr-url: https://github.com/nodejs/node/pull/37216 description: "Using `fs.rmdirSync(path, { recursive: true })` on a `path` that does not exist is no longer permitted and results in a `ENOENT` error." - version: v16.0.0 pr-url: https://github.com/nodejs/node/pull/37302 description: The `recursive` option is deprecated, using it triggers a deprecation warning. - version: v14.14.0 pr-url: https://github.com/nodejs/node/pull/35579 description: The `recursive` option is deprecated, use `fs.rmSync` instead. - version: - v13.3.0 - v12.16.0 pr-url: https://github.com/nodejs/node/pull/30644 description: The `maxBusyTries` option is renamed to `maxRetries`, and its default is 0. ... Returns `undefined`. ### `fs.statSync(path[, options])` <!-- YAML added: v0.1.21 changes: - version: - v15.3.0 - v14.17.0 pr-url: https://github.com/nodejs/node/pull/33716 description: Accepts a `throwIfNoEntry` option to specify whether an exception should be thrown if the entry does not exist. - version: v10.5.0 pr-url: https://github.com/nodejs/node/pull/20220 description: Accepts an additional `options` object to specify whether the numeric values returned should be bigint. - version: v7.6.0 pr-url: https://github.com/nodejs/node/pull/10739 description: The `path` parameter can be a WHATWG `URL` object using `file:` protocol. --> * `path` {string|Buffer|URL} * `options` {Object} * `bigint` {boolean} Whether the numeric values in the returned {fs.Stats} object should be `bigint`.
This option represents the number of retries. This option is ignored if the `recursive` option is not `true`. **Default:** `0`. * `recursive` {boolean} If `true`, perform a recursive directory removal. In recursive mode operations are retried on failure. **Default:** `false`. * `retryDelay` {integer} The amount of time in milliseconds to wait between retries. This option is ignored if the `recursive` option is not `true`. **Default:** `100`. * Returns: {Promise} Fulfills with `undefined` upon success. Removes files and directories (modeled on the standard POSIX `rm` utility). ### `fsPromises.stat(path[, options])` <!-- YAML added: v10.0.0 changes: - version: v26.8.0 pr-url: https://github.com/nodejs/node/pull/63143 description: Accepts an additional `signal` option to allow aborting the operation. - version: v25.7.0 pr-url: https://github.com/nodejs/node/pull/61178 description: Accepts a `throwIfNoEntry` option to specify whether an exception should be thrown if the entry does not exist. - version: v10.5.0 pr-url: https://github.com/nodejs/node/pull/20220 description: Accepts an additional `options` object to specify whether the numeric values returned should be bigint. --> * `path` {string|Buffer|URL} * `options` {Object} * `bigint` {boolean} Whether the numeric values in the returned {fs.Stats} object should be `bigint`. **Default:** `false`. * `throwIfNoEntry` {boolean} Whether an exception will be thrown if no file system entry exists, rather than returning `undefined`. **Default:** `true`. * `signal` {AbortSignal} An AbortSignal to cancel the operation. **Default:** `undefined`. * Returns: {Promise} Fulfills with the {fs.Stats} object for the given `path`. ### `fsPromises.statfs(path[, options])` <!-- YAML added: - v19.6.0 - v18.15.0 --> * `path` {string|Buffer|URL} * `options` {Object} * `bigint` {boolean} Whether the numeric values in the returned {fs.StatFs} ob…[truncated]</excerpt>
</source>
<source kind="search">
<title>statSync with throwIfNoEntry: false still throws ENOTDIR · Issue #56993 · nodejs/node · GitHub</title>
<location>GitHub issue 56993 in nodejs/node (link omitted to avoid creating a cross-reference)</location>
<excerpt>citeturn0search1 [wordlim: 200] Published: 1.7 years ago; Crawled: last month; at least Node 20+ ... > fs.statSync('./a/b', { throwIfNoEntry: false }) // throws ENOTDIR error ... a file exists in part of the path. ... to join this conversation on GitHub.
# statSync with throwIfNoEntry: false still throws ENOTDIR #56993
New issue
Copy link
New issue
Copy link
Closed
Closed
statSync with throwIfNoEntry: false still throws ENOTDIR#56993
Copy link
Labels
fsIssues and PRs related to file-system APIs and the fs module.Issues and PRs related to file-system APIs and the fs module.
## Description
jdalton
opened on Feb 10, 2025
### Version
at least Node 20+
### Platform
`osx
`
### Subsystem
No response
### What steps will reproduce the bug?
in your terminal of choice
> touch a
then enter into `node` repl
> fs.statSync('./a/b', { throwIfNoEntry: false }) // throws ENOTDIR error
### How often does it reproduce? Is there a required condition?
a file exists in part of the path.
### What is the expected behavior? Why is that the expected behavior?
I would expect `undefined` to be returned instead of erroring.
### What do you see instead?
it throws a ENOTDIR error.
## Activity
to join this conversation on GitHub. Already have an account? Sign in to comment
## Metadata
## Metadata
### Assignees
No one assigned
### Labels
fsIssues and PRs related to file-system APIs and the fs module.Issues and PRs related to file-system APIs and the fs module.
### Type
No type
### Projects
No projects
### Milestone
No milestone
### Relationships
None yet
### Development
No branches or pull requests</excerpt>
</source>
<omitted_sources count="10">Additional source records were omitted from this bounded result.</omitted_sources>
</source_evidence>
Citations:
- 1: https://nodejs.org/download/release/v20.19.0/docs/api/fs.html
- 2: https://nodejs.org/download/release/v20.19.0/docs/api/fs.html
- 3: https://nodejs.org/download/release/v20.19.0/docs/api/fs.html
<source_evidence>
<source kind="open_page">
<title>fs: handle UV_ENOTDIR in `fs.statSync` with `throwIfNoEntry` provided · nodejs/node@409e28d · GitHub</title>
<location>https://github.com/nodejs/node/commit/409e28d</location>
<excerpt>citeturn1view0 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({"ref_id":"https://github.com/nodejs/node/commit/409e28d","lineno":null}); Total lines: 233
L0: 【0†Skip to content】
L1: ## Navigation Menu
L2:
L3: 【1†Sign in】
L4:
L5: Appearance settings
L6:
L7: * Platform
L8:
L9: * AI CODE CREATION
L10: * 【2†GitHub CopilotWrite better code with AI】
L11: * 【3†GitHub Copilot appDirect agents from issue to merge】
L12: * 【4†MCP RegistryIntegrate external tools】
L13:
L14: * DEVELOPER WORKFLOWS
L15: * 【5†ActionsAutomate any workflow】
L16: * 【6†CodespacesInstant dev environments】
L17: * 【7†IssuesPlan and track work】
L18: * 【8†Code ReviewManage code changes】
L19: * 【9†Code QualityEnforce quality at merge】
L20: * APPLICATION SECURITY
L21: * 【10†GitHub Advanced SecurityFind and fix vulnerabilities】
L22: * 【11†Code securitySecure your code as you build】
L23: * 【12†Secret protectionStop leaks before they start】
L24:
L25: * EXPLORE
L26: * 【13†Why GitHub】
L27: * 【14†Documentation†docs.github.com】
L28: * 【15†Blog†github.blog】
L29: * 【16†Changelog†github.blog】
L30: * 【17†Marketplace】
L31:
L32: 【18†View all features】
L33:
L34: * Solutions
L35: * BY COMPANY SIZE
L36: * 【19†Enterprises】
L37: * 【20†Small and medium teams】
L38: * 【21†Startups】
L39: * 【22†Nonprofits】
L40:
L41: * BY USE CASE
L42: * 【23†App Modernization】
L43: * 【24†DevSecOps】
L44: * 【25†DevOps】
L45: * 【26†CI/CD】
L46: * 【27†View all use cases】
L47:
L48: * BY INDUSTRY
L49: * 【28†Healthcare】
L50: * 【29†Financial services】
L51: * 【30†Manufacturing】
L52: * 【31†Government】
L53: * 【32†See all industries】
L54:
L55: 【33†View all solutions】
L56:
L57: * Resources
L58: * EXPLORE BY TOPIC
L59: * 【34†AI】
L60: * 【35†Software Development】
L61: * 【36†DevOps】
L62: * 【37†Security】
L63: * 【38†View all topics】
L64:
L65: * EXPLORE BY TYPE
L66: * 【39†Customer stories】
L67: * 【40†Events & webinars】
L68: * 【41†Ebooks & reports】
L69: * 【42†Business insights】
L70: * 【43†GitHub Skills†skills.github.com】
L71: * SUPPORT & SERVICES
L72: * 【14†Documentation†docs.github.com】
L73: * 【44†Customer support†support.github.com】
L74: * 【45†Community forum】
L75: * 【46†Trust center】
L76: * 【47†Partners】
L77:
L78: 【48†View all resources】
L79:
L80: * Open Source
L81:
L82: * COMMUNITY
L83: * 【49†GitHub SponsorsFund open source developers】
L84: * PROGRAMS
L85: * 【50†Security Lab†securitylab.github.com】
L86: * 【51†Maintainer Community†maintainers.github.com】
L87: * 【52†GitHub Stars†stars.github.com】
L88: * 【53†Archive Program†archiveprogram.github.com】
L89:
L90: * REPOSITORIES
L91: * 【54†Topics】
L92: * 【55†Trending】
L93: * 【56†Collections】
L94:
L95: * Enterprise
L96:
L97: * ENTERPRISE SOLUTIONS
L98: * 【19†Enterprise platformAI-powered developer platform】
L99: * AVAILABLE ADD-ONS
L100: * 【10†GitHub Advanced SecurityEnterprise-grade security features】
L101: * 【57†Copilot for BusinessEnterprise-grade AI features】
L102: * 【58†Premium SupportEnterprise-grade 24/7 support】
L103:
L104: * 【59†Pricing】
L105:
L106: Search`/`
L107:
L108: 【1†Sign in】
L109:
L110: 【60†Sign up】
L111:
L112: Appearance settings
L113: You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. Dismiss alert
L114:
L115: 1. 【61†nodejs】
L116: 2. 【62†node】
L117: ## Repository navigation
L118:
L119: * * 【62†Code】
L120: * 【63†Issues632 (632)】
L121: * 【64†Pull requests663 (663)】
L122: * 【…[truncated]</excerpt>
</source>
<source kind="open_page">
<title>fs: handle UV_ENOTDIR in `fs.statSync` with `throwIfNoEntry` provided · nodejs/node@40f3a51 · GitHub</title>
<location>https://github.com/nodejs/node/commit/40f3a516</location>
<excerpt>citeturn1view1 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({"ref_id":"https://github.com/nodejs/node/commit/40f3a516","lineno":null}); Total lines: 233
L0: 【0†Skip to content】
L1: ## Navigation Menu
L2:
L3: 【1†Sign in】
L4:
L5: Appearance settings
L6:
L7: * Platform
L8:
L9: * AI CODE CREATION
L10: * 【2†GitHub CopilotWrite better code with AI】
L11: * 【3†GitHub Copilot appDirect agents from issue to merge】
L12: * 【4†MCP RegistryIntegrate external tools】
L13:
L14: * DEVELOPER WORKFLOWS
L15: * 【5†ActionsAutomate any workflow】
L16: * 【6†CodespacesInstant dev environments】
L17: * 【7†IssuesPlan and track work】
L18: * 【8†Code ReviewManage code changes】
L19: * 【9†Code QualityEnforce quality at merge】
L20: * APPLICATION SECURITY
L21: * 【10†GitHub Advanced SecurityFind and fix vulnerabilities】
L22: * 【11†Code securitySecure your code as you build】
L23: * 【12†Secret protectionStop leaks before they start】
L24:
L25: * EXPLORE
L26: * 【13†Why GitHub】
L27: * 【14†Documentation†docs.github.com】
L28: * 【15†Blog†github.blog】
L29: * 【16†Changelog†github.blog】
L30: * 【17†Marketplace】
L31:
L32: 【18†View all features】
L33:
L34: * Solutions
L35: * BY COMPANY SIZE
L36: * 【19†Enterprises】
L37: * 【20†Small and medium teams】
L38: * 【21†Startups】
L39: * 【22†Nonprofits】
L40:
L41: * BY USE CASE
L42: * 【23†App Modernization】
L43: * 【24†DevSecOps】
L44: * 【25†DevOps】
L45: * 【26†CI/CD】
L46: * 【27†View all use cases】
L47:
L48: * BY INDUSTRY
L49: * 【28†Healthcare】
L50: * 【29†Financial services】
L51: * 【30†Manufacturing】
L52: * 【31†Government】
L53: * 【32†See all industries】
L54:
L55: 【33†View all solutions】
L56:
L57: * Resources
L58: * EXPLORE BY TOPIC
L59: * 【34†AI】
L60: * 【35†Software Development】
L61: * 【36†DevOps】
L62: * 【37†Security】
L63: * 【38†View all topics】
L64:
L65: * EXPLORE BY TYPE
L66: * 【39†Customer stories】
L67: * 【40†Events & webinars】
L68: * 【41†Ebooks & reports】
L69: * 【42†Business insights】
L70: * 【43†GitHub Skills†skills.github.com】
L71: * SUPPORT & SERVICES
L72: * 【14†Documentation†docs.github.com】
L73: * 【44†Customer support†support.github.com】
L74: * 【45†Community forum】
L75: * 【46†Trust center】
L76: * 【47†Partners】
L77:
L78: 【48†View all resources】
L79:
L80: * Open Source
L81:
L82: * COMMUNITY
L83: * 【49†GitHub SponsorsFund open source developers】
L84: * PROGRAMS
L85: * 【50†Security Lab†securitylab.github.com】
L86: * 【51†Maintainer Community†maintainers.github.com】
L87: * 【52†GitHub Stars†stars.github.com】
L88: * 【53†Archive Program†archiveprogram.github.com】
L89:
L90: * REPOSITORIES
L91: * 【54†Topics】
L92: * 【55†Trending】
L93: * 【56†Collections】
L94:
L95: * Enterprise
L96:
L97: * ENTERPRISE SOLUTIONS
L98: * 【19†Enterprise platformAI-powered developer platform】
L99: * AVAILABLE ADD-ONS
L100: * 【10†GitHub Advanced SecurityEnterprise-grade security features】
L101: * 【57†Copilot for BusinessEnterprise-grade AI features】
L102: * 【58†Premium SupportEnterprise-grade 24/7 support】
L103:
L104: * 【59†Pricing】
L105:
L106: Search`/`
L107:
L108: 【1†Sign in】
L109:
L110: 【60†Sign up】
L111:
L112: Appearance settings
L113: You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. Dismiss alert
L114:
L115: 1. 【61†nodejs】
L116: 2. 【62†node】
L117: ## Repository navigation
L118:
L119: * * 【62†Code】
L120: * 【63†Issues632 (632)】
L121: * 【64†Pull requests664 (664)】
L122: * …[truncated]</excerpt>
</source>
<source kind="open_page">
<title>statSync with throwIfNoEntry: false still throws ENOTDIR · Issue #56993 · nodejs/node · GitHub</title>
<location>GitHub issue 56993 in nodejs/node (link omitted to avoid creating a cross-reference)</location>
<excerpt>citeturn1view2 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({"ref_id":"https://github.com/nodejs/node/issues/56993","lineno":null}); Total lines: 403
L0: 【0†Skip to content】
L1: ## Navigation Menu
L2:
L3: 【1†Sign in】
L4:
L5: Appearance settings
L6:
L7: * Platform
L8:
L9: * AI CODE CREATION
L10: * 【2†GitHub CopilotWrite better code with AI】
L11: * 【3†GitHub Copilot appDirect agents from issue to merge】
L12: * 【4†MCP RegistryIntegrate external tools】
L13:
L14: * DEVELOPER WORKFLOWS
L15: * 【5†ActionsAutomate any workflow】
L16: * 【6†CodespacesInstant dev environments】
L17: * 【7†IssuesPlan and track work】
L18: * 【8†Code ReviewManage code changes】
L19: * 【9†Code QualityEnforce quality at merge】
L20: * APPLICATION SECURITY
L21: * 【10†GitHub Advanced SecurityFind and fix vulnerabilities】
L22: * 【11†Code securitySecure your code as you build】
L23: * 【12†Secret protectionStop leaks before they start】
L24:
L25: * EXPLORE
L26: * 【13†Why GitHub】
L27: * 【14†Documentation†docs.github.com】
L28: * 【15†Blog†github.blog】
L29: * 【16†Changelog†github.blog】
L30: * 【17†Marketplace】
L31:
L32: 【18†View all features】
L33:
L34: * Solutions
L35: * BY COMPANY SIZE
L36: * 【19†Enterprises】
L37: * 【20†Small and medium teams】
L38: * 【21†Startups】
L39: * 【22†Nonprofits】
L40:
L41: * BY USE CASE
L42: * 【23†App Modernization】
L43: * 【24†DevSecOps】
L44: * 【25†DevOps】
L45: * 【26†CI/CD】
L46: * 【27†View all use cases】
L47:
L48: * BY INDUSTRY
L49: * 【28†Healthcare】
L50: * 【29†Financial services】
L51: * 【30†Manufacturing】
L52: * 【31†Government】
L53: * 【32†See all industries】
L54:
L55: 【33†View all solutions】
L56:
L57: * Resources
L58: * EXPLORE BY TOPIC
L59: * 【34†AI】
L60: * 【35†Software Development】
L61: * 【36†DevOps】
L62: * 【37†Security】
L63: * 【38†View all topics】
L64:
L65: * EXPLORE BY TYPE
L66: * 【39†Customer stories】
L67: * 【40†Events & webinars】
L68: * 【41†Ebooks & reports】
L69: * 【42†Business insights】
L70: * 【43†GitHub Skills†skills.github.com】
L71: * SUPPORT & SERVICES
L72: * 【14†Documentation†docs.github.com】
L73: * 【44†Customer support†support.github.com】
L74: * 【45†Community forum】
L75: * 【46†Trust center】
L76: * 【47†Partners】
L77:
L78: 【48†View all resources】
L79:
L80: * Open Source
L81:
L82: * COMMUNITY
L83: * 【49†GitHub SponsorsFund open source developers】
L84: * PROGRAMS
L85: * 【50†Security Lab†securitylab.github.com】
L86: * 【51†Maintainer Community†maintainers.github.com】
L87: * 【52†GitHub Stars†stars.github.com】
L88: * 【53†Archive Program†archiveprogram.github.com】
L89:
L90: * REPOSITORIES
L91: * 【54†Topics】
L92: * 【55†Trending】
L93: * 【56†Collections】
L94:
L95: * Enterprise
L96:
L97: * ENTERPRISE SOLUTIONS
L98: * 【19†Enterprise platformAI-powered developer platform】
L99: * AVAILABLE ADD-ONS
L100: * 【10†GitHub Advanced SecurityEnterprise-grade security features】
L101: * 【57†Copilot for BusinessEnterprise-grade AI features】
L102: * 【58†Premium SupportEnterprise-grade 24/7 support】
L103:
L104: * 【59†Pricing】
L105:
L106: Search`/`
L107:
L108: 【1†Sign in】
L109:
L110: 【60†Sign up】
L111:
L112: Appearance settings
L113: You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. Dismiss alert
L114:
L115: 1. 【61†nodejs】
L116: 2. 【62†node】
L117: ## Repository navigation
L118:
L119: * * 【62†Code】
L120: * 【63†Issues632 (632)】
L121: * 【64†Pull requests663 (663)】
L122: * 【65…[truncated]</excerpt>
</source>
<source kind="search">
<title>statSync with throwIfNoEntry: false still throws ENOTDIR · Issue #56993 · nodejs/node · GitHub</title>
<location>GitHub issue 56993 in nodejs/node (link omitted to avoid creating a cross-reference)</location>
<excerpt>citeturn0search4 [wordlim: 200] Published: 1.7 years ago; Crawled: last month; # statSync with throwIfNoEntry: false still throws ENOTDIR #56993 ... > fs.statSync('./a/b', { throwIfNoEntry: false }) // throws ENOTDIR error
# statSync with throwIfNoEntry: false still throws ENOTDIR #56993
New issue
Copy link
New issue
Copy link
Closed
Closed
statSync with throwIfNoEntry: false still throws ENOTDIR#56993
Copy link
Labels
fsIssues and PRs related to file-system APIs and the fs module.Issues and PRs related to file-system APIs and the fs module.
## Description
jdalton
opened on Feb 10, 2025
### Version
at least Node 20+
### Platform
`osx
`
### Subsystem
No response
### What steps will reproduce the bug?
in your terminal of choice
> touch a
then enter into `node` repl
> fs.statSync('./a/b', { throwIfNoEntry: false }) // throws ENOTDIR error
### How often does it reproduce? Is there a required condition?
a file exists in part of the path.
### What is the expected behavior? Why is that the expected behavior?
I would expect `undefined` to be returned instead of erroring.
### What do you see instead?
it throws a ENOTDIR error.
## Activity
to join this conversation on GitHub. Already have an account? Sign in to comment
## Metadata
## Metadata
### Assignees
No one assigned
### Labels
fsIssues and PRs related to file-system APIs and the fs module.Issues and PRs related to file-system APIs and the fs module.
### Type
No type
### Projects
No projects
### Milestone
No milestone
### Relationships
None yet
### Development
No branches or pull requests</excerpt>
</source>
<source kind="search">
<title>Releases · nodejs/node · GitHub</title>
<location>https://github.com/nodejs/node/releases</location>
<excerpt>citeturn0search0 [wordlim: 200] Crawled: 4 days ago; * [`2a3548e51c`] - deps: update Undici to 6.28.1 (mcollina) #65790 ... * [[`89aa2bd10c`](https://github.com/nodejs/node/commi...
# Releases: nodejs/node
Releases Tags
## Release list
* 2026-10-21, Version 26.12.1 (Current), @aduh95 prepared by @RafaelGSS
* 2026-10-21, Version 24.22.1 'Krypton' (LTS), @aduh95
* 2026-10-21, Version 22.23.4 'Jod' (LTS), @aduh95
* 2026-10-14, Version 26.12.0 (Current), @richardlau
* 2026-10-14, Version 24.22.0 'Krypton' (LTS), @richardlau prepared by @aduh95
* 2026-10-07, Version 26.11.1 (Current), @aduh95
* 2026-10-07, Version 26.11.0 (Current), @aduh95
* 2026-09-23, Version 22.23.3 'Jod' (LTS), @aduh95 prepared by @juanarbol
* 2026-09-22, Version 26.10.0 (Current), @aduh95
* 2026-09-16, Version 26.9.0 (Current), @aduh95
Jump to release
## 2026-10-21, Version 26.12.1 (Current), @aduh95 prepared by @RafaelGSS
2026-10-21, Version 26.12.1 (Current), @aduh95 prepared by @RafaelGSS
Latest
Compare
# Choose a tag to compare
## Sorry, something went wrong.
Filter
Loading [Input]
## Sorry, something went wrong.
### Uh oh!
There was an error while loading. Please reload this page.
## No results found
View all tags
Image: @aduh95 aduh95 released this 21 Oct 10:38
v26.12.1
This tag was signed with the committer’s verified signature.
aduh95 Antoine du Hamel
GPG key ID: 20B1A390B168D356
Verified
Learn about vigilant mode.
`bc2e01d`
This commit was signed with the committer’s verified signature.
aduh95 Antoine du Hamel
GPG key ID: 20B1A390B168D356
Verified
Learn about vigilant mode.
This is a security release.
### Notable Changes
* (CVE-2026-106069, CVE-2026-106072) tls: reject non-canonical IP addresses (Matteo Collina) – High
* (CVE-2026-105482) dns: validate resolve inputs for null bytes (James M Snell) – High
* (CVE-2026-105483) tls: fix ip normalization regression (James M Snell) – High
* (CVE-2026-111497) src: expose `BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH` (Antoine du Hamel) – High
* (CVE-2026-111497) deps: brotli: cherry-pick 0ed4268a4401abcb44720963 (Evgenii Kliuchnikov) – High
* (CVE-2026-105481) http: ensure agent data guard works without I/O (James M Snell) – Medium
* (CVE-2026-106074) crypto: reset random caches across snapshots (Filip Skokan) – Medium
* (CVE-2026-106070) zlib: avoid caching invalid ZIP local headers (Matteo Collina) – Low
* (CVE-2026-106071) src: fix the fs permission tree node on wildcard nodes (James M Snell) – Low
* (CVE-2026-106073) permission: check permission model in realpathSync (RafaelGSS) – Low
* deps: V8: backport 93cd21e8254b (Joyee Cheung)
* crypto: update root certificates to NSS 3.131.1 (Antoine du Hamel)
* tls: reject chains containing blocked public keys (Ulises Gascon)
* deps: update c-ares to 1.34.9 (Node.js GitHub Bot)
* tools: retry Gatekeeper check and stapling after notarization (Ryan Aslett)
* crypto: harden X.509 conversion and formatting (Filip Skokan)
### Commits
* [`818e4ca061`] - crypto: update root certificates to NSS 3.131.1 (Antoine du Hamel) #66877
* [`fd3c87ed43`] - crypto: harden X.509 conversion and formatting (Filip Skokan) nodejs-private/node-private#966
* [`acffa610db`] - crypto: reset random caches across snapshots (Filip Skokan) nodejs-private/node-private#987
* [`7895a5cd2d`] - deps: update undici to 8.12.2 (Node.js GitHub Bot) #66721
* [`12daa54d2e`] - deps: V8: backport 93cd21e8254b (Joyee Cheung) nodejs-private/node-private#999
* [`6a8b3db7e2`] - deps: update c-ares to 1.34.9 (Node.js GitHub Bot) #66868
* [`a6040b50d9`] - deps: brotli: cherry-pick 0ed4268a4401abcb44720963 (Evgenii Kliuchnikov) nodejs-private/node-private#1002
* [`6b78ea171d`] - dns: validate resolve inputs for null bytes (James M Snell) nodejs-private/node-private#984
## 2026-10-14, Version 26.12.0 (Current), @richardlau
2026-10-14, Version 26.12.0 (Current), @…[truncated]</excerpt>
</source>
<omitted_sources count="23">Additional source records were omitted from this bounded result.</omitted_sources>
</source_evidence>
Citations:
- 1: https://github.com/nodejs/node/commit/409e28d
- 2: GitHub issue 56993 in nodejs/node (link omitted to avoid creating a cross-reference)
- 3: https://github.com/nodejs/node/commit/409e28d
Script executed:
set -o pipefail
sed -n '10845,10905p' node_modules/.pnpm/vitest@5.0.5_patch_hash=a8c4c8af3636bf5cc481bd7ebcaa39c47a6dcc3d12fb3f66559953fe3e8671e_2246e3bf613442cffb3138208c8ec861/node_modules/vitest/dist/chunks/cli-api.De07GUeT.js
sed -n '85,115p' node_modules/.pnpm/vitest@5.0.5_patch_hash=a8c4c8af3636bf5cc481bd7ebcaa39c47a6dcc3d12fb3f66559953fe3e8671e_2246e3bf613442cffb3138208c8ec861/node_modules/vitest/dist/chunks/init.PvxYpuCm.js
Repository: stablyai/orca
Length of output: 3361
Catch non-ENOENT errors from the stat guard.
parseStacktrace calls getSourceMap for each frame. On supported Node versions, statSync(file, { throwIfNoEntry: false }) can throw ENOTDIR for a path with a file component. The exception reaches _reportFileTask, which records it as an "Unhandled Reporter Error" instead of ignoring the frame. Catch the stat failure and skip source-map lookup.
Suggested fix
- if (!project.vite.moduleGraph.getModuleById(file)?.transformResult && statSync(file, { throwIfNoEntry: false })?.isFile()) {+ if (project.vite.moduleGraph.getModuleById(file)?.transformResult) return;+ let isFile;+ try {+ isFile = statSync(file, { throwIfNoEntry: false })?.isFile();+ } catch {+ return;+ }+ if (isFile) {
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Unit shards sometimes fail with every test passing:
When Vitest annotates a task error, it looks up a source map for each stack frame. The check is
existsSync(file)followed byreadFileSync(file). If a frame's path is a directory,existsSyncreturns true,readFileSyncthrowsEISDIR, and Vitest reports it as an unhandled error. The run exits 1, and the original test result is swallowed.This PR extends our existing
vitest@5.0.5patch: the source-map lookup now reads the file only whenstatSync(...).isFile(). 5.0.5 is the latest Vitest release, so there's no upstream fix to adopt. The lockfile change is only the patch hash.Impact
Across the 100 most recent failed
pr.ymlruns (~4.5 h on 2026-10-27), 5 of 32 failed unit-shard jobs were pureEISDIRflakes with zero failing tests. That's ~25 spurious red PR runs/day, each needing a shard rerun plus a re-wait onverify. One hit #34324 today.Verification
Locally, a throwaway test throwing an error whose stack names a directory:
Unhandled Errors … EISDIR,Test Files (1)with no result,Errors 1 errorTest Files 1 failed (1), the real test failure, with no unhandled errorCI on this PR runs the full unit suite on the patched Vitest.