fix(api): centralize 403 Forbidden enrichment with actionable hints (CLI-1JG)#892
Merged
Conversation
Two bugs caused 400 Bad Request errors from the Sentry API: 1. listTransactions/listSpans passed --limit directly as per_page. Values >100 were rejected by the API. Now caps at API_MAX_PER_PAGE and auto-paginates when limit exceeds 100, matching the queryEvents pattern in discover.ts. (CLI-F3, CLI-10) 2. timeRangeToApiParams returned only start or only end for open-ended absolute ranges (e.g. --period '>=2024-01-01'). The API requires both. Now fills end=now for start-only, start=end-90d for end-only. Fixes all consumers (traces, spans, explore). (CLI-10)
Extract the duplicated multi-page pagination loop from listTransactions, listSpans, and queryEvents into a generic autoPaginate<T>() helper in infrastructure.ts. This addresses Cursor Bugbot's feedback about code duplication across the three call sites. Also adds comprehensive tests for listTransactions and listSpans (26 tests covering URL construction, per_page capping, auto-pagination, trimming, sort, project handling, and cursor passthrough) to push patch coverage above 80%.
…LI-1JG) Previously only 4 of 60+ API call sites enriched 403 errors with scope/token guidance. All other endpoints surfaced a raw unhelpful 'You do not have permission to perform this action.' message. Add enrich403Detail() in infrastructure.ts as a centralized chokepoint that enriches all 403 ApiErrors with: - Env-var tokens: specific missing scopes + token settings URL - OAuth tokens: re-authentication suggestion Add enriched403 flag to ApiError to prevent double-enrichment where command-layer code already has specialized 403 handling. Remove redundant enrichListOrgsForbidden() from organizations.ts. Guard issue list's build403Detail() to skip scope hints when centralized enrichment already applied.
Contributor
|
Contributor
Codecov Results 📊✅ 6410 passed | Total: 6410 | Pass Rate: 100% | Execution Time: 0ms 📊 Comparison with Base Branch
All tests are passing successfully. ✅ Patch coverage is 83.76%. Project has 13189 uncovered lines. Files with missing lines (5)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
+ Coverage 75.95% 76.02% +0.07%
==========================================
Files 295 295 —
Lines 55028 55009 -19
Branches 0 0 —
==========================================
+ Hits 41794 41820 +26
- Misses 13234 13189 -45
- Partials 0 0 —Generated by Codecov Action |
… enrichment
When the API returns { detail: null } or { detail: undefined },
stringifyUnknown() would produce the literal string 'undefined' or
'null', which leaked into the enriched error message. Now falls back
to stringifying the whole error object instead.
Also add tests for undefined/null detail edge cases and fix env var
cleanup symmetry in OAuth test.
…nriched When centralized 403 enrichment has already added scope/token hints, re-throw the ApiError directly instead of passing the multi-line enriched detail into build403Error, which would nest it as a single messy bullet in a ResolutionError.
…lish
When throwApiError receives { detail: undefined } or { detail: null },
the fallback stringifyUnknown(error) produces "{}" or {"detail":null}
which leaked as the first visible line before the enrichment hints.
Now passes undefined to enrich403Detail for 403s with nullish detail,
so the enrichment stands alone without a noisy prefix. Non-403 errors
keep the fallback behavior for debugging.
The nullish detail fix from the previous commit only covered
throwApiError (SDK path). throwRawApiError (raw fetch path via
apiRequestToRegion) had the same issue: { detail: null } would
produce {"detail":null} as visible output in 403 enrichment.
Now checks typeof parsed.detail before using it, and skips the
JSON.stringify fallback for 403 errors.
BYK
added a commit
that referenced
this pull request
Apr 30, 2026
…#897) ## Summary - Use `getUTCDate()`/`setUTCDate()` instead of `getDate()`/`setDate()` for the 90-day date backfill in `timeRangeToApiParams()` - The input is a UTC ISO 8601 string, so date arithmetic should use UTC methods to avoid off-by-one-day errors in non-UTC timezones Flagged by Cursor Bugbot on #892 ([comment](#892 (comment))).
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
throwApiError()/throwRawApiError()so all 60+ API endpoints get actionable error messages instead of the raw"You do not have permission to perform this action."https://sentry.io/settings/auth-tokens/sentry auth loginenriched403flag toApiErrorto prevent double-enrichment where command-layer code already has specialized 403 handling (issue list, dashboard, project delete)Fixes CLI-1JG — a user hit
sentry org viewwith an auto-detected org and got a completely unhelpful 403 error.Before
After (env-var token)
After (OAuth token)