Skip to content

feat(http): add option to retry only unprocessed mutations - #225

Merged
Vincent Biret (baywet) merged 1 commit into
microsoft:mainfrom
jackson-asmith:fix/retry-unprocessed-mutations-only
Nov 13, 2026
Merged

Vincent Biret (baywet) merged 1 commit into
microsoft:mainfrom
jackson-asmith:fix/retry-unprocessed-mutations-only

Conversation

@jackson-asmith

Copy link
Copy Markdown
Contributor

Fixes #224

This ports microsoft/kiota-dotnet#864 to Ruby. The other ports are microsoft/kiota-http-go#312, microsoft/kiota-python#897, microsoft/kiota-java#2251, microsoft/kiota-typescript#2230, microsoft/kiota-php#143 and microsoft/kiota-dart#179.

Problem

RetryHandler retries 429, 503 and 504 responses whatever the HTTP method. For POST and PATCH, a retry after a 504 can apply the request twice. A 504 means a gateway stopped waiting, not that the server did nothing, so the first attempt may already have succeeded. With Microsoft Graph's sendMail, this sends the same email twice.

A caller can avoid this today with a custom should_retry that checks response.env.method. Most callers, including users of the generated SDKs, never set should_retry, though, so they keep the retries that can send twice. A built-in option makes the safe behavior a single setting.

Change

This adds a retry_unprocessed_mutations_only keyword to RetryHandlerOption, matching the .NET option.

  • Default is false. Existing behavior doesn't change.
  • When true, POST and PATCH are retried only after a 429 or 503. By common service convention, those responses mean the server didn't process the request. Any other response is returned to the caller, even when a custom should_retry accepts it.
  • Other methods aren't affected. GET, PUT and DELETE keep retrying as before.
  • The check runs on every attempt, so a POST that gets a 503 and then a 504 stops at the 504.
  • It works as a client-wide option or per request, through the request context like the other retry settings.
option = MicrosoftKiotaFaraday::Middleware::RetryHandlerOption.new(retry_unprocessed_mutations_only: true)

KiotaClientFactory adds RedirectHandler before RetryHandler, so the redirect handler wraps the retry handler and the retry handler only sees the request for the current redirect hop. Each retry resends that request, so its method is always the one being resent.

Tests

The new retry_unprocessed_mutations_spec.rb has 18 examples, using Faraday's test adapter with the real RedirectHandler and RetryHandler, covering:

  • the default
  • POST and PATCH with 504, 429 and 503
  • a custom should_retry that accepts a 500
  • method matching that ignores case
  • GET, PUT and DELETE
  • the 503 then 504 sequence
  • the per-request option
  • a POST that a 303 turns into a GET, which is retried after a 504, and a POST kept by a 307, which isn't

With the new check removed, the 6 examples for the new behavior fail and the other 12 pass. Locally on Ruby 4.0, bundle exec rake for the http component (145 examples) and bundle exec rubocop from the repo root pass.

Add RetryHandlerOption's retry_unprocessed_mutations_only keyword. It
defaults to false, which keeps the current behavior. When it is true,
POST and PATCH requests are retried only after a 429 or 503 response,
which indicate the server did not process the request, even when a
custom should_retry accepts other responses. A 504 is returned to the
caller, because the server may already have applied the request and a
retry could apply it twice. Other methods are unaffected.
Copilot AI balanced review requested due to automatic review settings November 13, 2026 18:05

Copilot AI 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.

🟢 Approval recommended

No active review issues block approval of the opt-in retry-protection change.

No open issues 🎉

What changed in this PR

Adds opt-in retry protection to Kiota Ruby’s HTTP middleware to reduce duplicate POST/PATCH side effects after ambiguous failures, addressing #224 while preserving existing behavior by default.

Changes:

  • Adds and documents retry_unprocessed_mutations_only, defaulting to false.
  • When enabled, restricts POST/PATCH retries to 429 and 503 responses while retaining existing retry checks.
  • Adds regression coverage for defaults, custom policies, per-request options, repeated attempts, and redirects.
File Description
components/​http/​spec/​microsoft_kiota_faraday/​middleware/​retry_unprocessed_mutations_spec.rb Tests retry safety, compatibility, configuration, and redirect behavior.
components/​http/​lib/​microsoft_kiota_faraday/​middleware/​retry_handler.rb Applies method-specific retry restrictions on every attempt.
components/​http/​lib/​microsoft_kiota_faraday/​middleware/​retry_handler_option.rb Exposes the option and documents its default, risks, and service assumptions.

Review effort: Balanced

@baywet
Vincent Biret (baywet) added this pull request to the merge queue Nov 13, 2026
Merged via the queue into microsoft:main with commit d50eafd Nov 13, 2026
120 checks passed
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.

RetryHandler retries non-idempotent POST requests on 504, which can duplicate side effects such as Graph sendMail

3 participants