Skip to content

fix(cli): Keep timeout guidance and name the real cause of network errors - #1904

Merged
betegon merged 8 commits into
mainfrom
fix/cli-network-error-guidance
Nov 4, 2026
Merged

betegon merged 8 commits into
mainfrom
fix/cli-network-error-guidance

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes the messaging and telemetry behind CLI-29S ("Failed to create release: Network error").

unwrapResult re-threw only AuthError/ApiError from the fetch layer. throwApiError then wrapped everything else as Network error … Check your internet connection and try again. Three problems came from that:

  • Our own TimeoutError lost its guidance. A request timeout already carries a hint that points at Sentry's status, but the wrapper replaced it with "check your internet connection". Any CliError thrown by the fetch layer now passes through unchanged (this also covers HostScopeError).
  • The real cause was dropped. For a connection failure, undici rejects with TypeError: fetch failed and puts the errno (ENOTFOUND, ECONNREFUSED, …) on cause, which was never shown. The detail now names the Sentry URL, the errno message, and a next step for that errno. It ends with sentry status on SaaS, or a SENTRY_URL/SENTRY_HOST check on self-hosted.
  • The silencing added for CLI-16W was bypassed. It only matched the raw TypeError, and SDK-backed calls never surface the raw error. The wrapper now keeps the original on cause, and classifySilenced treats a wrapped fetch failed as network_error. TLS errors (ApiError(0) without that cause) and non-network causes, such as a local SQLite unable to open database file, stay captured.

A review question: silencing means ECONNRESET-style failures against sentry.io show up only in the silenced-error metric, not as issues. Timeouts stay captured as TimeoutError.

Before / after

Real sentry release create runs from source, on the base commit and on this branch. For the demo the request timeout was cut to 2s (it is 30s), and the sentry.io case was simulated by routing through a local port that never answers. [exit N] is the exit code.

Before: every case printed the same thing. Connection failures showed Cause: fetch failed. Timeouts showed Cause: Request timed out after 2s., with the same "check your internet connection" advice and exit code 30.

$ SENTRY_URL=https://sentry.acme-corp.invalid sentry release create my-org/1.0.0
Error: Failed to create release: Network error
  Unable to reach Sentry API. Cause: fetch failed

  Check your internet connection and try again.
[exit 30]

After: each message names the server, the real cause, and the next step that fits it, in the CLI's existing error formats:

  • Network errors keep the Network error title (so grouping is unchanged) and use the ApiError detail layout from the 401/403 enrichment: an indented detail, a blank line, then one step per line, with commands as Verb with: <command>.
  • Timeouts keep TimeoutError's layout and exit code (31): a sentence, then an indented action line, like the View in Sentry:/Or retry: lines in the issue explain and issue plan timeout hints.
  • The check step is the CLI's own sentry status command. For a self-hosted timeout it's sentry status --url <server>, which probes /_health/. The login hint uses getUrlLoginCommand(), like the other login hints.

Self-hosted, hostname doesn't resolve:

$ SENTRY_URL=https://sentry.acme-corp.invalid sentry release create my-org/1.0.0
Error: Failed to create release: Network error
  Unable to reach Sentry at https://sentry.acme-corp.invalid: getaddrinfo ENOTFOUND sentry.acme-corp.invalid

  The hostname could not be resolved. Check that the URL is right and that DNS works on this machine.
  This URL comes from SENTRY_URL. If it is not your Sentry server, set SENTRY_URL to the right URL.
[exit 30]

Self-hosted, server not running:

$ SENTRY_URL=http://127.0.0.1:59999 sentry release create my-org/1.0.0
Error: Failed to create release: Network error
  Unable to reach Sentry at http://127.0.0.1:59999: connect ECONNREFUSED 127.0.0.1:59999

  The server refused the connection. Check that the URL and port are right and that Sentry is running.
  This URL comes from SENTRY_URL. If it is not your Sentry server, set SENTRY_URL to the right URL.
[exit 30]

Self-hosted, server accepts the connection but never answers:

$ SENTRY_URL=http://127.0.0.1:8765 sentry release create my-org/1.0.0
Error: Failed to create release: Request timed out after 2s.

The Sentry API at http://127.0.0.1:8765 did not respond in time. The change may still have been applied; check before trying again.
  Check the server's health with: sentry status --url http://127.0.0.1:8765
[exit 31]

sentry.io never answers:

$ sentry release create my-org/1.0.0
Error: Failed to create release: Request timed out after 2s.

The Sentry API at https://sentry.io did not respond in time. The change may still have been applied; check before trying again.
  Check Sentry's status with: sentry status
[exit 31]

The CLI's API requests use plain Node fetch, which ignores HTTPS_PROXY/HTTP_PROXY unless NODE_USE_ENV_PROXY is set. So these messages don't name a proxy, and a proxy variable never replaces the hint for the actual error.

via bete.

--

View Session [Sentry]

…rors

SDK-backed calls wrapped every fetch-layer failure as "Network error ...
Check your internet connection", including our own TimeoutError (which
already points at status.sentry.io) and raw undici "fetch failed" errors
whose real errno sits on `cause`. The wrapper also bypassed the
network-error silencing added for CLI-16W, so these all landed in CLI-29S.

- Re-throw any CliError from the fetch layer unchanged in unwrapResult.
- Name the Sentry URL, the underlying errno, and a matching next step
  (status page on SaaS, SENTRY_URL check on self-hosted).
- Keep the original error on `cause` and silence wrapped "fetch failed".

Co-Authored-By: Miguel Betegon <miguel.betegon@sentry.io>
@vercel

vercel Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
sentry-local Skipped Skipped Nov 4, 2026 9:56am UTC

Request Review

@github-actions github-actions Bot added the risk: medium PR risk score: medium label Nov 3, 2026
Network error guidance now resolves the Sentry URL the same way request
routing does (env, then the stored login's host), so a self-hosted login
without SENTRY_URL is no longer told it failed to reach sentry.io.

Since unwrapResult now rethrows TimeoutError instead of wrapping it as
ApiError(0), `org list` skips an account whose server times out (as it
does for other unreachable servers), and alert list failures keep the
timeout's class and hint.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

The all-targets-failed branch of `issue list` only preserved ApiError and
wrapped everything else in a plain Error. Now that unwrapResult rethrows
TimeoutError unchanged, rethrow it with the prefix so its status-page
hint and timeout exit code survive.
SENTRY_HOST takes precedence over SENTRY_URL, so telling a user with
SENTRY_HOST set to change SENTRY_URL pointed them at a no-op.
Checked against real CLI runs:

- Network errors show the innermost cause instead of "fetch failed",
  including causes without an errno (e.g. "bad port").
- Self-hosted: say where the URL comes from (SENTRY_HOST, SENTRY_URL, or
  the stored login) and how to change it, plus a curl command to test
  the connection.
- With HTTPS_PROXY/HTTP_PROXY set, point at the proxy (by variable name,
  never its value) instead of the errno hint, which then describes the
  proxy connection.
- Timeouts name the operation ("Failed to create release: Request timed
  out after 30s."), like ApiError does, and only point self-hosted users
  at their own server, not status.sentry.io.

Co-Authored-By: Miguel Betegon <miguel.betegon@sentry.io>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3771ca. Configure here.

The API transport calls plain Node fetch, which ignores HTTPS_PROXY and
HTTP_PROXY unless NODE_USE_ENV_PROXY is set, and the hint ignored NO_PROXY.
Naming a proxy the CLI did not use hid the real errno guidance, so keep the
errno-specific next step whatever proxy variables are set.
Follow the CLI's own hint patterns: suggest the `sentry status` command
(with --url for self-hosted, which probes /_health/) instead of the
status page URL, put the timeout's next step on an indented
'Verb with: command' line like other TimeoutError hints, and build the
login hint with getUrlLoginCommand so it names the running program.
sentry-mcp has no status command, so it keeps the URL fallbacks.

Co-Authored-By: Miguel Betegon <miguel.betegon@sentry.io>
Comment thread packages/cli/src/lib/sentry-client.ts Resolved
Comment thread packages/cli/src/lib/sentry-client.ts Resolved
Comment thread packages/cli/src/lib/sentry-client.ts Resolved
Hardcode `sentry status` like the CLI's other hints, without per-program
fallbacks, and drop the curl suggestion from self-hosted network errors.
@betegon
betegon merged commit b8c480f into main Nov 4, 2026
28 checks passed
@betegon
betegon deleted the fix/cli-network-error-guidance branch November 4, 2026 14:18

This branch was previously deployed

1 inactive deployment
Preview — 9d1f2549 Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant