Skip to content

Retry GraphQL requests rejected as Throttled without a 429 code - #8317

Open
isaacroldan wants to merge 3 commits into
mainfrom
isaac/retry-throttled-graphql
Open

Retry GraphQL requests rejected as Throttled without a 429 code#8317
isaacroldan wants to merge 3 commits into
mainfrom
isaac/retry-throttled-graphql

Conversation

@isaacroldan

Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

retryAwareRequest retries rate-limited requests, but only recognizes HTTP 429 or a GraphQL error with extensions.code === '429'. Some Shopify APIs — App Management among them — throttle with a 200 response whose GraphQL error message is Throttled, carrying no status and no code. Those requests fail instantly on first rejection.

Evidence: E2E CI jobs run with DEBUG=1, and jobs that hit Throttled contain zero retries exhausted / succeeded after N retries debug lines — the retry path never engaged. This is currently the top E2E failure mode (32 of 79 failed shards last week, per the failure-mode dataset), and real shopify app dev / app deploy users hit the same instant failure.

WHAT is this pull request doing?

Extends the retryability check (errorsIncludeStatus429errorsIncludeThrottling) to also match GraphQL errors whose message starts with Throttled. Everything else reuses the existing machinery: up to 10 retries, Retry-After when present, 1s default backoff otherwise.

How to test your changes?

packages/cli-kit/src/private/node/api.test.ts adds a case mirroring the App Management shape (200 + {message: 'Throttled'}, no code, no retry-after) and asserts the request is retried and succeeds.

Measuring impact

The e2e-failure-report dataset tracks partners-throttled as a failure mode — it should drop sharply once this lands.

🤖 Generated with Claude Code

retryAwareRequest already retries rate-limited requests, but only when
the response is HTTP 429 or a GraphQL error with extensions.code '429'.
Some Shopify APIs (App Management among them) throttle with a 200
response whose GraphQL error message is "Throttled" and no code, so the
CLI failed instantly on first rejection. CI logs from throttled E2E jobs
running with DEBUG=1 show zero retry attempts, confirming the path was
never taken.

Match the "Throttled" message as retryable too, reusing the existing
retry limit and default backoff. This currently accounts for the top
E2E failure mode (32 of 79 failed shards last week) and affects real
`app dev`/`app deploy` users the same way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@isaacroldan
isaacroldan requested a review from a team as a code owner August 12, 2026 14:36
@github-actions github-actions Bot added the Area: @shopify/cli @shopify/cli package issues label Aug 12, 2026
@github-actions github-actions Bot added no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users. and removed Area: @shopify/cli @shopify/cli package issues labels Aug 12, 2026
// the message so those are retried too.
return (
error.response.errors?.some(
(graphqlError) => graphqlError.extensions?.code === '429' || /^throttled/i.test(graphqlError.message ?? ''),

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.

Regex tests always raise some flags... Is there nothing like a canonical error code we can use? Or at least some tighter enforcement around the message text? So someone can't name an app "throttled" and then retry on user errors...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants