Skip to content

fix(errors): classify network errors as exit code 4#4

Merged
facundofarias merged 1 commit intomainfrom
fix/classify-network-errors
Apr 28, 2026
Merged

fix(errors): classify network errors as exit code 4#4
facundofarias merged 1 commit intomainfrom
fix/classify-network-errors

Conversation

@facundofarias
Copy link
Copy Markdown
Contributor

@facundofarias facundofarias commented Apr 28, 2026

Summary

  • Add output.NetworkError (exit code 4) and output.IsNetworkErr helper
  • Detects standard transport failures: net.Error.Timeout(), *net.DNSError, *net.OpError, *url.Error chains, and ECONNREFUSED / ECONNRESET / EHOSTUNREACH / ENETUNREACH / EPIPE syscalls
  • ClassifyError now applies the same detection to raw errors that bubble up unwrapped
  • The two known wrap sites (auth login and hello both validating credentials via client.ListProjects) now return NetworkError when the cause is connectivity-related

Why

Mixpanel telemetry currently buckets every API-call failure as error_class=internal because every command path wrapped errors as *output.InternalError unconditionally. That made the "internal" bucket unreadable — a transient DNS timeout looked identical to a real CLI bug. Today's data: auth login shows 6 `internal` failures in 30 days from 4 users on 4 different versions, no obvious server-side cause. With this change, those will split between true internal (CLI bugs) and network (transport failures), so the "internal" bucket becomes a real signal worth investigating.

Scope

  • New type & helper centralized in internal/output/errors.go
  • auth.go and hello.go opt into it
  • Other command sites (mcp.go, setup.go, config.go, etc.) wrap genuinely-local operations (file I/O, keyring, term reads) — they're correctly internal and don't need to change
  • ClassifyError's fallback detection means any future API-call site that doesn't explicitly classify will still pick up network automatically when applicable

Test plan

  • go test ./... — green
  • go vet ./... — clean
  • golangci-lint run (latest) — 0 issues
  • Unit tests cover: nil err, generic err, syscall errnos (5 of them), *net.DNSError, *net.OpError, net.Error.Timeout(), *url.Error wrapping a network cause, *url.Error wrapping a non-network cause (negative case), and ClassifyError picking up a raw *net.OpError and *url.Error chain
  • Verify in Mixpanel after merge that a deliberately-unreachable run (`DEPLOYHQ_HOST=unreachable.invalid dhq auth login --account x --email x --api-key x`) shows up as `error_class=network` instead of `internal`

Notes

This pairs with #3 (sanitized error_message in telemetry) — together they give us "which class" + "what specifically" for every failure.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Improved error handling during authentication and credential validation. The system now correctly identifies and reports network connectivity issues (timeouts, DNS errors, etc.) separately from other errors, providing clearer feedback to users experiencing temporary network problems.

Mixpanel telemetry currently buckets every API-call failure as
error_class=internal because the only command paths that wrapped errors
returned *output.InternalError unconditionally. That made the "internal"
bucket impossible to read — a transient DNS timeout looked identical to
a real CLI bug.

Add a NetworkError type (exit code 4 = network) and an IsNetworkErr
helper that detects the standard transport-level failures from
net/http: net.Error.Timeout(), *net.DNSError, *net.OpError, *url.Error
chains, and ECONNREFUSED / ECONNRESET / EHOSTUNREACH / ENETUNREACH /
EPIPE syscalls. ClassifyError now applies the same detection to raw
errors that bubble up unwrapped, so commands that don't explicitly wrap
their errors still classify correctly.

The two known sites that wrap a network call (auth.go and hello.go,
both validating credentials via client.ListProjects) now return
NetworkError when the cause is connectivity-related. Successful HTTP
exchanges that returned non-2xx status (e.g. 401, 5xx) are unchanged
and still flow through the existing AuthError / InternalError paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

The PR adds network error detection across the application. A new NetworkError type and IsNetworkErr classifier function are introduced in the output package to identify connectivity failures (timeouts, DNS errors, syscall errors, etc.). The auth and hello commands are updated to distinguish network failures from internal errors when credential validation fails.

Changes

Cohort / File(s) Summary
Network Error Infrastructure
internal/output/errors.go, internal/output/errors_test.go
Added NetworkError type and IsNetworkErr function to classify connectivity failures including timeouts, DNS errors, net.OpError, url.Error, and syscall errors. Updated ClassifyError to map network errors to ExitNetworkError. Comprehensive test coverage validates detection logic across multiple error types.
Command Error Handling
internal/commands/auth.go, internal/commands/hello.go
Updated runAuthLogin and helloLogin to detect network failures when client.ListProjects errors. Now returns NetworkError for connectivity issues instead of treating them as internal errors, while preserving unauthorized error handling.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix(errors): classify network errors as exit code 4' accurately and concisely summarizes the main change: adding network error classification with exit code 4. It directly reflects the core objective to distinguish network failures from other error types.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/classify-network-errors

Comment @coderabbitai help to get the list of available commands and usage tips.

@facundofarias facundofarias merged commit 24d15b1 into main Apr 28, 2026
2 of 3 checks passed
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: a7eb29667e

ℹ️ 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/commands/auth.go
Comment on lines +134 to +135
if output.IsNetworkErr(err) {
return &output.NetworkError{Message: "validate credentials", Cause: 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.

P2 Badge Map network failures to network_error JSON code

Returning &output.NetworkError{...} here causes ErrorResponseFromErr to emit code: "error" in JSON mode because that switch only handles UserError, AuthError, and InternalError (see internal/output/breadcrumbs.go, ErrorResponseFromErr). This regresses machine-readable semantics for these login network failures: clients no longer get the documented network_error code and retryable may be false for non-timeout transport errors like connection refused.

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.

1 participant