Repository navigation
feat(cli): Retry writes Sentry marks idempotent - #1706
Merged
Merged
Conversation
Read idempotency from the @sentry/api operation catalog instead of hard-coding it at each call site. Sentry now publishes chunk-upload and the DIF and artifact bundle assemble endpoints with x-sentry-idempotent, and @sentry/api 0.420.0 exposes it as `idempotent` on each operation. generate:schema writes the marked non-safe operations to src/generated/idempotent-operations.json, and the authenticated fetch retries any request that matches one. The per-call `idempotent: true` opt-ins for those endpoints are gone. Preprod assemble keeps its opt-in because Sentry does not mark it. Bumps @sentry/api from 0.400.0 to 0.420.0 for the CLI and MCP. Refs getsentry/sentry#128927
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
apps/local imports CLI modules that reach sentry-client.ts, and its CI job never runs generate:schema, so the gitignored generated JSON was missing there. Track the 3-entry file like banner-sixel.ts. The build still regenerates it from @sentry/api.
BYK
requested changes
Oct 28, 2026
Drop the generated idempotent-operations.json. The CLI now imports @sentry/api/operations lazily on the first write, so nothing generated is checked in and code outside the CLI needs no generate step. Chunk uploads keep their explicit opt-in: they post to the URL the server's chunk-upload options return, which need not match the catalog path.
Go back to having generate:schema write the marked writes to the gitignored src/generated/idempotent-operations.json instead of shipping the whole @sentry/api catalog in the bundle. The apps/local tests import CLI modules that reach sentry-client.ts and ran before anything generated the file. Its test script now runs ensure-generated.mjs first, the same bootstrap the CLI's own scripts use. Chunk uploads keep their explicit opt-in: they post to the URL the server's chunk-upload options return, which need not match the catalog path.
BYK
approved these changes
Oct 28, 2026
This was referenced Oct 28, 2026
BYK
added a commit
that referenced
this pull request
Oct 29, 2026
Follow-up to #1677 and #1673, and the last part of #1669 task 5.0. The CLI's interim `retry-policy.ts` duplicated `@sentry/toolkit-core/api-retry`, with small differences between the two. The CLI now uses the shared module, so the CLI and MCP have one rule for which statuses and methods get retried and how long a 429 waits. Each still sets its own limits: the CLI's `RETRY_POLICY` keeps 2 retries, a 1s base and a 10s per-wait cap, and adds a 30s total. Changes in `toolkit-core/src/api-retry.ts`: - New export `isIdempotentRequest(method, idempotent)`. The CLI uses it for the timeout error wording and for `generate-api-schema`'s idempotent-operation filter. - `nextRetryDelayMs` now accepts `status: undefined` for a network error or timeout, which is retried only for idempotent requests. The MCP always passes a status, so its behavior is unchanged. Behavior changes in the CLI, all from taking on the shared rule: - **408 is no longer retried.** The shared list is 429/500/502/503/504, as #1669's 5.0 spec says. Sentry doesn't send 408 itself. - **Clock skew:** with a skewed local clock, the wait is now clamped into the 1s window allowed by the response `Date` header, instead of being measured from `Date`. One `sentry-client` test asserted the old ≥900ms wait; it now asserts that the wait stays inside that window and not the hour of skew. - **Retry-After:** when both `Retry-After` and the rate-limit reset are present, `Retry-After` wins. Before, the CLI took the longer of the two. - **Jitter:** a server-requested wait gets up to `baseDelayMs / 2` of extra delay. Backoff without a server hint works the same as before. Two things stay the same: 401 token refresh still works for any method, and writes that Sentry marks idempotent (#1706) still retry. <!-- junior-request-attribution:start --> via **BYK**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:slack%3AC08J1NSPU6S%3A1793057920.879309 --> -- [View Junior Session](https://junior-prod.sentry.dev/conversations/slack%3AC08J1NSPU6S%3A1793057920.879309) [[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC08J1NSPU6S%3A1793057920.879309/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: BYK <burak.kaya@sentry.io>
This branch was successfully deployed
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.
The CLI now reads which writes are safe to replay from the
@sentry/apioperation catalog. Before, each call site hard-codedidempotent: true.Sentry publishes chunk-upload and the DIF and artifact bundle assemble endpoints as
PUBLIC_EXPERIMENTAL, withx-sentry-idempotentset (sentry#128999).@sentry/apiexposes that asidempotenton each operation (sentry-api-schema#111).What changes
@sentry/apigoes from0.400.0to0.420.0in the pnpm catalog, so the CLI and MCP both get it.generate:schemaalso writessrc/generated/idempotent-operations.json. The file lists the non-safe operations that the catalog marksidempotent, which today are 3 POSTs. It is a separate file so the CLI does not load the 140 KB endpoint index on every request. Likebanner-sixel.ts, it is committed, because code outside the CLI (such asapps/local) importssentry-client.tswithout runninggenerate:schema. Commit it again after an@sentry/apibump. The build regenerates it either way.isIdempotentOperation()(src/lib/idempotent-operations.ts) matches a request URL against those path templates. It matches from/api/onward, so self-hosted installs served under a path prefix still match.fetchWithRetrytreats a match like an explicit opt-in.idempotent: true: chunk uploads, DIF assemble (debug files, ProGuard, Dart symbols), and artifact bundle assemble (source maps,pollAssembly).There is no behavior change for these endpoints today. They retried before because of the opt-in, and now they retry because Sentry marks them. If Sentry marks or unmarks an endpoint later, the next
@sentry/apibump picks it up.Verification
pnpm run tsc(whole workspace), CLIpnpm run lint, and rootpnpm run lintall pass.apps/localvitest (theLocalCI job) passes with only the committed generated files present: 38 files, 322 tests.mainin my sandbox. They are file-permission tests (EACCES/EPERM, read-only dirs).test/lib/idempotent-operations.test.tscovers matching, the self-hosted prefix, case, and the trailing slash. It also checks that unmarked writes (preprod assemble, autofix) don't match and that a template segment can't span two path segments.sentry-clienttests check that a marked POST retries after a 503 and after a network error with no opt-in. I confirmed both fail when the catalog lookup is removed.Refs getsentry/sentry#128927
--
View Junior Session in Sentry