Skip to content

fix: save engine stderr to temp log and show --exclude hint on failure#20

Merged
takai merged 1 commit intomainfrom
fix/silent-exit
Mar 7, 2026
Merged

fix: save engine stderr to temp log and show --exclude hint on failure#20
takai merged 1 commit intomainfrom
fix/silent-exit

Conversation

@takai
Copy link
Copy Markdown
Owner

@takai takai commented Mar 7, 2026

Closes #19

Problem

When the engine subprocess fails on a large diff, the error shown to the user is not actionable:

  • Claude: exits 1 with empty stderr → engine command failed: exit status 1: (no information)
  • Codex: exits 1 with the entire prompt echoed to stderr, burying the real error at the end → unreadable wall of text

Both cases left the user with no idea what went wrong or how to fix it.

Solution

engine.EngineError (new type in internal/engine/cli.go)

CLI.Generate() now returns a structured *EngineError on non-zero exit instead of embedding stderr into the error string. The Stderr field preserves the full output separately, accessible via errors.As.

Actionable error message (internal/app/app.go)

When the engine fails, buildEngineFailureError():

  1. Saves the full stderr to a temp file (/tmp/git-ai-commit-stderr-*.log) — even when stderr is empty. The file is kept for the user to inspect.
  2. Shows a --exclude hint when the diff had truncated or pattern-excluded files, suggesting the likely culprits for a context window overflow.

Example output:

engine command failed: exit status 1
Full engine output saved to: /tmp/git-ai-commit-stderr-3728164819.log
Hint: the following files were truncated or excluded and may have caused
a context window overflow. Re-run with --exclude to skip them:
  git ai-commit --exclude 1m_lines.txt

Changes

  • internal/engine/cli.goEngineError type; Generate() returns *EngineError on failure
  • internal/engine/cli_test.go — unit tests for EngineError
  • internal/app/app.gobuildEngineFailureError, writeTempLog, buildExcludeCandidates helpers; commitDiff now also returns git.Result
  • internal/app/app_test.go — unit tests for the new helpers

@takai takai merged commit c2e0f71 into main Mar 7, 2026
1 check passed
@takai takai deleted the fix/silent-exit branch March 7, 2026 05:35
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 309efbbd3c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/app/app.go
Comment on lines +252 to +253
_, _ = f.WriteString(content)
return f.Name()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check temp log writes before reporting saved output

writeTempLog unconditionally returns the temp file path even when WriteString fails, so buildEngineFailureError can tell users “Full engine output saved to …” when the log is actually empty or truncated. This is reproducible on ENOSPC or other write errors and removes the only place stderr is preserved in this flow, making failures harder to debug in exactly the environments where diagnostics are most needed.

Useful? React with 👍 / 👎.

Comment thread internal/app/app.go
Comment on lines +266 to +268
for _, f := range filterResult.ExcludedFiles {
if !excluded[f] {
candidates = append(candidates, f)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude hint must avoid already pattern-excluded files

buildExcludeCandidates appends filterResult.ExcludedFiles directly, but those files were already excluded by git.Filter and therefore cannot be further reduced by adding --exclude again. When engine execution fails, this produces no-op remediation steps (for example default-excluded lockfiles), which can generate long misleading hints and distract from the actionable files that are actually still in the prompt.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tool exits silently when large files appear in diff

1 participant