Skip to content

[Feat] Machine-friendly exit codes for error classification#59

Merged
samzong merged 3 commits intomainfrom
fix/exit-codes
Mar 30, 2026
Merged

[Feat] Machine-friendly exit codes for error classification#59
samzong merged 3 commits intomainfrom
fix/exit-codes

Conversation

@samzong
Copy link
Copy Markdown
Owner

@samzong samzong commented Mar 30, 2026

What's changed?

  • Add internal/exitcode package defining typed exit codes: NoStagedChanges=10, NotGitRepo=11, LLMError=12
  • main.go extracts exitcode.Error via errors.As before falling back to os.Exit(1)
  • Sentinel errors git.ErrNotGitRepo and llm.ErrLLM enable errors.Is classification
  • Shared classifyError() in cmd package eliminates duplication between root and tag commands
  • Unit tests cover all three exit code paths (10, 11, 12)

Why

  • All errors currently exit 1, forcing agents/CI to parse stderr text to distinguish error categories
  • Structured exit codes let automation retry transient LLM failures (12) vs report user errors (10, 11) without text parsing

Closes #55

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a centralized exit code management system by adding an internal exitcode package and updating error handling in the git, llm, and cmd packages. The review feedback identifies a potential compilation issue on Go versions earlier than 1.20 due to the use of multiple error-wrapping verbs and suggests wrapping unclassified errors in the tag command with userFacingError to maintain consistent formatting across the CLI.

Comment thread internal/llm/llm.go

if err != nil {
return "", fmt.Errorf("failed to call LLM: %w", err)
return "", fmt.Errorf("failed to call LLM: %w (%w)", err, ErrLLM)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The use of multiple %w format verbs for error wrapping was introduced in Go 1.20. If this project needs to maintain compatibility with older Go versions, this will cause a compilation error.

For compatibility with Go versions < 1.20, you could wrap only ErrLLM and include the original error's text in the message. This preserves the ability to check for ErrLLM while keeping the context.

For example:

return "", fmt.Errorf("failed to call LLM: %v: %w", err, ErrLLM)

This would mean errors.As would no longer work for the original err, but errors.Is for ErrLLM would still function correctly.

Comment thread internal/llm/llm.go

if err != nil {
return "", "", fmt.Errorf("failed to call LLM: %w", err)
return "", "", fmt.Errorf("failed to call LLM: %w (%w)", err, ErrLLM)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Similar to the comment on line 104, the use of multiple %w verbs for error wrapping requires Go 1.20+. If compatibility with older versions is needed, consider an alternative wrapping strategy.

For example:

return "", "", fmt.Errorf("failed to call LLM: %v: %w", err, ErrLLM)

Comment thread cmd/tag.go
if coded := classifyError(err); coded != nil {
return coded
}
return err
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To ensure consistent error message formatting across all commands, unclassified errors from the tag command should also be wrapped in userFacingError, similar to how the root command handles them. This will provide a uniform experience for users, with errors prefixed by gmc: .

After making this change, please also ensure all error return paths in runTagCommand use this wrapper for consistency (e.g., the one at line 70 seems to have been missed).

Suggested change
return err
return userFacingError{msg: fmt.Sprintf("gmc: %v", err), err: err}

@samzong samzong merged commit 85eb5d2 into main Mar 30, 2026
1 check passed
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.

feat: standardize machine-friendly exit codes

1 participant