Skip to content

HttpRetryOptions.from_json raises KeyError on wire payloads missing optional retry fields or using snake_case keys #351

Closed as not planned

Description

Bug Description

In azure.durable_functions.http.models.HttpRetryOptions.from_json(), the deserializer assumes all optional fields are present as exact camelCase keys in the dictionary payload:

@classmethod
def from_json(cls, value: dict[str, Any]) -> "HttpRetryOptions":
    max_interval = value["maxRetryIntervalInMilliseconds"]
    timeout = value["retryTimeoutInMilliseconds"]
    return cls(
        first_retry_interval=timedelta(milliseconds=value["firstRetryIntervalInMilliseconds"]),
        max_number_of_attempts=value["maxNumberOfAttempts"],
        backoff_coefficient=value["backoffCoefficient"],
        max_retry_interval=timedelta(milliseconds=max_interval) if max_interval is not None else None,
        retry_timeout=timedelta(milliseconds=timeout) if timeout is not None else None,
        status_codes_to_retry=value.get("statusCodesToRetry"))

When a payload omits optional fields (such as maxRetryIntervalInMilliseconds, retryTimeoutInMilliseconds, or backoffCoefficient when default values were used, or when serialized across language boundaries), from_json() raises KeyError:

KeyError: 'maxRetryIntervalInMilliseconds'

Additionally, unlike DurableHttpResponse.from_json(), which defensively accepts both status_code and statusCode, HttpRetryOptions.from_json() does not support snake_case key naming (first_retry_interval_in_milliseconds, max_number_of_attempts, backoff_coefficient, etc.), failing with KeyError if snake_case dictionaries are supplied.

Expected Behavior

HttpRetryOptions.from_json() should safely parse dictionary payloads with missing optional fields, falling back to the class constructor's documented defaults (backoff_coefficient=1.0, max_retry_interval=timedelta(days=6), retry_timeout=None, status_codes_to_retry=None). It should also support both camelCase and snake_case key conventions defensively.

Activity

  1. andystaples commented on Nov 11, 2026

    @andystaples
    Contributor

    Thanks for the report. We're going to decline this change.

    • No compatibility need: HttpRetryOptions serialization hasn't shipped in a release yet, so there's no older payload shape to keep reading. We've only accepted snake_case reads elsewhere (e.g. Schedules hotfix for DTS dashboard #169) to keep reading state that earlier releases already wrote.
    • No producer of these shapes: these payloads are written by the SDK's own HttpRetryOptions.to_json(), which always writes every field in camelCase. We haven't found a supported producer that omits fields or writes snake_case keys.
    • Silent defaults are risky: filling in a missing first retry interval or attempt count would turn malformed input into a no-retry policy with no error. An explicit failure is preferable.

    If you can reproduce the KeyError through supported APIs, please open a new issue with a minimal repro: SDK versions, orchestrator code, and the exact retryOptions payload. We'll gladly take a look.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions