Summary
ProjectGitService captures stdout, stderr, and the git process exit code, but status() and diff() ignore non-zero exit codes and parse only stdout. When git fails, the web API can return HTTP 200 with an empty status or empty diff instead of surfacing the failure.
Code path
Producer / parser / feedback anchors:
src/adapters/web/projectGit.ts:32-36 declares the runner result shape as { stdout, stderr, code }.
src/adapters/web/projectGit.ts:45-47 runs git status --porcelain=v2 --branch and parses only stdout.
src/adapters/web/projectGit.ts:50-56 runs git diff and returns only stdout as diff.
src/adapters/web/projectGit.ts:59-79 captures the child process code, stdout, and stderr.
src/adapters/web/httpRouter.ts:99-109 sends /git/status and /git/diff service results with HTTP 200.
src/adapters/web/httpRouter.ts:112-118 only reports an error if the service throws.
src/gateway/server/GatewayServer.ts:95-100 binds /api/web/* to handleWebApiRequest.
Steps to reproduce
Validation level: dynamic minimal reproduction plus source control-flow confirmation.
Create a ProjectGitService pointing at a directory that is not a git repository and call status() / diff():
const svc = new ProjectGitService({ projectRoot: nonGitTempDir });
console.log(await svc.status());
console.log(await svc.diff("x.ts"));
Observed local output:
status {"files":[]}
diff {"path":"x.ts","diff":""}
A fake runner that explicitly returns code: 128 and stderr: "fatal: not a git repository" produces the same successful empty objects:
status {"files":[]}
diff {"path":"x.ts","diff":""}
Expected behavior
A non-zero git process exit code should be treated as a failed git operation. The service should preserve stderr and the API should return an error response instead of an empty successful result.
Actual behavior
The non-zero code and stderr are discarded. Empty stdout is interpreted as a valid empty status/diff, and the HTTP route would serialize that as success.
Existing coverage
I checked current issues/PRs for ProjectGitService, parsePorcelainV2, projectGit.ts, httpRouter.ts, /api/web/projects, git status, and git diff; no same-root issue/PR was found.
This is separate from #254 / #285, which cover web-search env API key resolution and do not touch git process failure feedback.
Suggested fix
Make the service throw on code !== 0, preserving enough context for API feedback. For example, centralize runner result handling before parsing stdout:
const result = await this.run(args);
if (result.code !== 0) {
throw new Error(result.stderr || `git ${args[0]} failed with exit code ${result.code}`);
}
Then map that error to a clear HTTP response in httpRouter rather than returning HTTP 200.
Suggested tests
ProjectGitService.status() throws or returns an error when runner returns code: 128.
ProjectGitService.diff() throws or returns an error when runner returns code: 128.
- The error preserves
stderr such as fatal: not a git repository.
- The
/git/status and /git/diff HTTP routes return an error response when the service reports git failure.
- Successful porcelain/diff output continues to parse as before.
Submitted with Codex.
Summary
ProjectGitServicecapturesstdout,stderr, and the git process exitcode, butstatus()anddiff()ignore non-zero exit codes and parse onlystdout. When git fails, the web API can return HTTP 200 with an empty status or empty diff instead of surfacing the failure.Code path
Producer / parser / feedback anchors:
src/adapters/web/projectGit.ts:32-36declares the runner result shape as{ stdout, stderr, code }.src/adapters/web/projectGit.ts:45-47runsgit status --porcelain=v2 --branchand parses onlystdout.src/adapters/web/projectGit.ts:50-56runsgit diffand returns onlystdoutasdiff.src/adapters/web/projectGit.ts:59-79captures the child processcode,stdout, andstderr.src/adapters/web/httpRouter.ts:99-109sends/git/statusand/git/diffservice results with HTTP 200.src/adapters/web/httpRouter.ts:112-118only reports an error if the service throws.src/gateway/server/GatewayServer.ts:95-100binds/api/web/*tohandleWebApiRequest.Steps to reproduce
Validation level: dynamic minimal reproduction plus source control-flow confirmation.
Create a
ProjectGitServicepointing at a directory that is not a git repository and callstatus()/diff():Observed local output:
A fake runner that explicitly returns
code: 128andstderr: "fatal: not a git repository"produces the same successful empty objects:Expected behavior
A non-zero git process exit code should be treated as a failed git operation. The service should preserve
stderrand the API should return an error response instead of an empty successful result.Actual behavior
The non-zero
codeandstderrare discarded. Emptystdoutis interpreted as a valid empty status/diff, and the HTTP route would serialize that as success.Existing coverage
I checked current issues/PRs for
ProjectGitService,parsePorcelainV2,projectGit.ts,httpRouter.ts,/api/web/projects,git status, andgit diff; no same-root issue/PR was found.This is separate from #254 / #285, which cover web-search env API key resolution and do not touch git process failure feedback.
Suggested fix
Make the service throw on
code !== 0, preserving enough context for API feedback. For example, centralize runner result handling before parsing stdout:Then map that error to a clear HTTP response in
httpRouterrather than returning HTTP 200.Suggested tests
ProjectGitService.status()throws or returns an error when runner returnscode: 128.ProjectGitService.diff()throws or returns an error when runner returnscode: 128.stderrsuch asfatal: not a git repository./git/statusand/git/diffHTTP routes return an error response when the service reports git failure.Submitted with Codex.