fs: add stack traces to async callback errors #61720
Open
+203
−4
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.
Human View
Problem
Async fs callback errors have no JavaScript stack traces — only the error message (#30944, open since 2019, 29 reactions). This makes debugging fs operations very difficult:
The root cause: errors are created in C++ (
FSReqAfterScope::Reject→UVException) when the JavaScript call stack is empty. The sync API and promises API don't have this problem — sync errors are created in JS context, and the promises API already callsErrorCaptureStackTraceinhandleErrorFromBinding.Solution
Two-tier approach that enriches async callback errors with stack traces:
Tier 1 (always-on, default):
enrichFsError()is called inmakeCallback()on the error path. Zero overhead on success (~2-5nsinstanceofcheck), captures internal Node.js frames on error.Tier 2 (opt-in via
NODE_FS_ASYNC_STACK_TRACES=1): Captures the JavaScript call-site stack at operation initiation time, providing full stack traces showing WHERE in user code the operation was called. ~1us overhead per async fs call when enabled.Before:
After (default):
After (
NODE_FS_ASYNC_STACK_TRACES=1):Changes
lib/internal/fs/utils.js: AddenrichFsError()andshouldCaptureCallSiteStack()— shared error enrichment utilitieslib/fs.js: ModifymakeCallback()andmakeStatsCallback()to enrich errors; add call-site capture toreadFile()lib/internal/fs/read/context.js: Add error enrichment toReadFileContextpathtest/parallel/test-fs-error-stack-trace.js: Comprehensive tests for all async fs operationsDesign Rationale
makeCallbackand not C++? Pure JavaScript change, same pattern as existinghandleErrorFromBinding(sync) and promises APIError.captureStackTracecosts ~1us per call. Forfs.stat(~2.5us), this adds 40% overhead. Opt-in avoids surprising perf regressions.NODE_FS_ASYNC_STACK_TRACES=1 node app.js).Fixes: #30944
AI View (DCCE Protocol v1.0)
Metadata
AI Contribution Summary
Verification Steps Performed
Human Review Guidance
lib/internal/fs/utils.js,lib/fs.js,lib/internal/fs/read/context.jsMade with M7 Cursor