fix(core): handle ENAMETOOLONG/ENOTDIR in robustRealpath (#26010)#26074
fix(core): handle ENAMETOOLONG/ENOTDIR in robustRealpath (#26010)#26074gaurav0107 wants to merge 1 commit intogoogle-gemini:mainfrom
Conversation
…ni#26010) Pasting text that the at-mention regex recognizes as a path — e.g. a JSON blob containing long hex strings or "@email.com"-style tokens — caused a CRITICAL unhandled Promise rejection: ENAMETOOLONG: name too long, lstat '<very long string>' `robustRealpath` only special-cased ENOENT / EISDIR in its catch branch; other error codes were rethrown. ENAMETOOLONG (path component exceeds OS limit) and ENOTDIR (intermediate component is a regular file) both mean "this is not a resolvable real path" — the same semantic as ENOENT — but the correct fallback is to return the input unchanged rather than walk up the parent chain (whose basename is garbage in these cases). Extend the catch handler to treat those two codes as non-fatal and return the caller's input, matching the behaviour downstream callers already expect when a probed path doesn't exist on disk. Also factor the repeated `typeof e === 'object' && 'code' in e && ...` checks into a local `hasErrorCode(e, codes)` helper for readability; semantics are unchanged for the existing ENOENT/EISDIR branch. Adds regression tests for both codes in paths.test.ts alongside the existing ENOENT/EISDIR coverage. Closes google-gemini#26010
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a critical issue where the CLI would crash due to unhandled promise rejections when processing user input that resembles a file path but is invalid or too long for the operating system to resolve. By extending the error handling logic in Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request refactors path resolution logic in packages/core by introducing a hasErrorCode utility for safer error handling. It updates robustRealpath to gracefully handle ENAMETOOLONG and ENOTDIR errors by returning the input path, which prevents crashes when the system encounters long strings or invalid directory structures during path probing. Corresponding regression tests have been added to ensure stability. I have no feedback to provide.
Summary
Stops the CLI from crashing with
CRITICAL: Unhandled Promise Rejection: ENAMETOOLONG: name too long, lstat '...'when pasting text that the at-mention regex recognizes as a path but that the OS cannot resolve.Details
packages/core/src/utils/paths.ts::robustRealpathwrapsfs.realpathSync(p)and only special-casedENOENT/EISDIR; every other error code was rethrown unhandled.The crash path:
@email.com-style token.atCommandProcessor.parseAllAtCommandsmatches it as anatPath(the regex is intentionally generous).checkPermissionscallsresolveToRealPath(path.resolve(targetDir, pathName)).robustRealpath->fs.realpathSync(p)->ENAMETOOLONG-> rethrown -> unhandled promise rejection in the UI layer -> CRITICAL crash banner.This PR extends the catch branch to treat two additional error codes as non-fatal:
ENAMETOOLONG— path component exceedsNAME_MAX/PATH_MAX.ENOTDIR— an intermediate component is a regular file (e.g.@path/to/file.json/extra).Both semantically mean "this is not a resolvable real path", same as
ENOENT. The correct fallback is to return the input unchanged so downstreamfileExists/fs.statcallers (which already handleENOENTgracefully, seeatCommandProcessor.ts:301) can reject it normally. Walking up the parent chain (as the existingENOENTbranch does for symlink-aware resolution) would be wrong here because the basename is garbage.Also factors the repeated
typeof e === 'object' && 'code' in e && ...checks into a localhasErrorCode(e, codes)helper. Semantics of the existingENOENT/EISDIRbranch are unchanged.Related Issues
Closes #26010
How to Validate
Unit tests covering the new branches:
End-to-end: paste a large JSON blob containing strings longer than 255 chars into the prompt — prior to this patch the CLI panics with an unhandled ENAMETOOLONG; after this patch the string is treated as plain text and no path resolution error surfaces.
Full local CI allowlist (all green on this branch):
(Two pre-existing failures in
packages/cli/src/config/extension-manager-*.test.tsreproduce on unmodified upstreammainand are unrelated to this change.)Pre-Merge Checklist
npm run lintcleannpm run typecheckclean@google/gemini-cli-coretest suite cleanresolveToRealPathsignature unchanged