fix(core): handle ENAMETOOLONG in robustRealpath

- Add ENAMETOOLONG to caught errors in robustRealpath to prevent crashes on long paths.
- Add regression test case to verify fix and prevent future regressions.

Fixes https://github.com/google-gemini/gemini-cli/issues/25696
This commit is contained in:
Taylor Mullen 2026-04-20 20:21:54 -07:00
parent 4b2091d402
commit 1902ae5b3f
2 changed files with 21 additions and 3 deletions

View file

@ -601,6 +601,22 @@ describe('resolveToRealPath', () => {
/Infinite recursion detected/, /Infinite recursion detected/,
); );
}); });
it('should return path as-is if fs.realpathSync and fs.lstatSync fail with ENAMETOOLONG', () => {
vi.spyOn(fs, 'realpathSync').mockImplementation(() => {
const err = new Error('File name too long') as NodeJS.ErrnoException;
err.code = 'ENAMETOOLONG';
throw err;
});
vi.spyOn(fs, 'lstatSync').mockImplementation(() => {
const err = new Error('File name too long') as NodeJS.ErrnoException;
err.code = 'ENAMETOOLONG';
throw err;
});
const longPath = path.resolve('a'.repeat(5000));
expect(resolveToRealPath(longPath)).toBe(longPath);
});
}); });
describe('makeRelative', () => { describe('makeRelative', () => {

View file

@ -424,7 +424,7 @@ function robustRealpath(p: string, visited = new Set<string>()): string {
e && e &&
typeof e === 'object' && typeof e === 'object' &&
'code' in e && 'code' in e &&
(e.code === 'ENOENT' || e.code === 'EISDIR') (e.code === 'ENOENT' || e.code === 'EISDIR' || e.code === 'ENAMETOOLONG')
) { ) {
try { try {
const stat = fs.lstatSync(p); const stat = fs.lstatSync(p);
@ -435,13 +435,15 @@ function robustRealpath(p: string, visited = new Set<string>()): string {
} }
} catch (lstatError: unknown) { } catch (lstatError: unknown) {
// Not a symlink, or lstat failed. Re-throw if it's not an expected // Not a symlink, or lstat failed. Re-throw if it's not an expected
// ENOENT (e.g., a permissions error), otherwise resolve parent. // error (e.g., a permissions error), otherwise resolve parent.
if ( if (
!( !(
lstatError && lstatError &&
typeof lstatError === 'object' && typeof lstatError === 'object' &&
'code' in lstatError && 'code' in lstatError &&
(lstatError.code === 'ENOENT' || lstatError.code === 'EISDIR') (lstatError.code === 'ENOENT' ||
lstatError.code === 'EISDIR' ||
lstatError.code === 'ENAMETOOLONG')
) )
) { ) {
throw lstatError; throw lstatError;