From f73389eac3eb8a12f41d0dd73643c158b29972a2 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:58:31 +0100 Subject: [PATCH] fix: ENOBUFS in detect_changes by setting maxBuffer on git/rg execFileSync (#957) * Initial plan * Fix ENOBUFS in detect_changes by setting maxBuffer on git/rg execFileSync Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/bb241ed0-3b39-431f-a242-b0c7ced9707b Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --- gitnexus/src/mcp/local/local-backend.ts | 11 ++++- .../test/unit/local-backend-maxbuffer.test.ts | 48 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 gitnexus/test/unit/local-backend-maxbuffer.test.ts diff --git a/gitnexus/src/mcp/local/local-backend.ts b/gitnexus/src/mcp/local/local-backend.ts index fe63ee1a2..0175928f6 100644 --- a/gitnexus/src/mcp/local/local-backend.ts +++ b/gitnexus/src/mcp/local/local-backend.ts @@ -1765,7 +1765,14 @@ export class LocalBackend { let diffOutput: string; try { - diffOutput = execFileSync('git', diffArgs, { cwd: repo.repoPath, encoding: 'utf-8' }); + // maxBuffer raised from Node's 1MB default to 256MB to avoid ENOBUFS on + // repos with large unstaged/untracked diffs (e.g. unignored build folders). + // See issue: spawnSync git ENOBUFS in detect_changes(scope="unstaged"). + diffOutput = execFileSync('git', diffArgs, { + cwd: repo.repoPath, + encoding: 'utf-8', + maxBuffer: 256 * 1024 * 1024, + }); } catch (err: any) { return { error: `Git diff failed: ${err.message}` }; } @@ -2039,6 +2046,8 @@ export class LocalBackend { cwd: repo.repoPath, encoding: 'utf-8', timeout: 5000, + // Avoid ENOBUFS on large repos: rg -l can list many files. + maxBuffer: 256 * 1024 * 1024, }); const files = output .trim() diff --git a/gitnexus/test/unit/local-backend-maxbuffer.test.ts b/gitnexus/test/unit/local-backend-maxbuffer.test.ts new file mode 100644 index 000000000..f67675021 --- /dev/null +++ b/gitnexus/test/unit/local-backend-maxbuffer.test.ts @@ -0,0 +1,48 @@ +/** + * Source-code regression: ENOBUFS on large git/rg output. + * + * Node's default maxBuffer for execFileSync is 1 MB, which is easily exceeded + * by `git diff` on repos with large unstaged changes (e.g. unignored build + * folders) — see the original bug report: + * + * "spawnSync git ENOBUFS in gitnexus_detect_changes(scope=\"unstaged\") + * due to missing maxBuffer". + * + * Every `execFileSync` call in `local-backend.ts` that captures stdout + * (i.e. sets `encoding`) MUST pass an explicit `maxBuffer`. This test is a + * lightweight static guard so the regression cannot silently come back. + * + * Kept as a standalone file (no LocalBackend import) so it does not depend + * on the LadybugDB native binding being available in the test environment. + */ +import { describe, it, expect } from 'vitest'; +import fs from 'fs'; +import path from 'path'; + +const SOURCE_PATH = path.join(__dirname, '../../src/mcp/local/local-backend.ts'); + +describe('local-backend: execFileSync maxBuffer regression', () => { + const source = fs.readFileSync(SOURCE_PATH, 'utf-8'); + + it('every stdout-capturing execFileSync call passes maxBuffer', () => { + // Match each `execFileSync(...)` call. The local-backend.ts call sites use + // a single trailing options object literal, so a non-greedy match up to the + // closing `)` of the statement is sufficient. + const callRe = /execFileSync\s*\(([\s\S]*?)\)\s*;/g; + const offenders: string[] = []; + let match: RegExpExecArray | null; + while ((match = callRe.exec(source)) !== null) { + const args = match[1]; + // Only stdout-capturing calls (encoding set) are at risk of ENOBUFS. + if (!/encoding\s*:/.test(args)) continue; + if (!/maxBuffer\s*:/.test(args)) { + const lineNo = source.slice(0, match.index).split('\n').length; + offenders.push(`line ${lineNo}: ${args.replace(/\s+/g, ' ').slice(0, 160)}`); + } + } + expect( + offenders, + `execFileSync calls missing explicit maxBuffer (ENOBUFS risk):\n${offenders.join('\n')}`, + ).toEqual([]); + }); +});