puppetlabs/puppet · error · Puppet::HTTP::ProtocolError
Failed to parse Retry-After header '%{retry_after}' as an in
Error message
Failed to parse Retry-After header '%{retry_after}' as an integer or RFC 2822 date What it means
Puppet's Retry-After parsing accepts exactly two forms: an integer number of seconds, or an RFC 2822 date-time such as 'Wed, 21 Aug 2026 07:28:00 GMT'. It first tries Integer(), then DateTime.rfc2822; if both raise, Puppet::HTTP::ProtocolError is raised naming the offending header value. Notably ISO 8601 timestamps ('2026-08-21T07:28:00Z') are RFC 2822-incompatible and fail.
Source
Thrown at lib/puppet/http/retry_after_handler.rb:75
# if retry-after is far in the future, we could end up sleeping repeatedly
# for 30 minutes, effectively waiting indefinitely, seems like we should wait
# in total for 30 minutes, in which case this upper limit needs to be enforced
# by the client.
[seconds, @max_sleep].min
end
private
def parse_retry_after(retry_after)
Integer(retry_after)
rescue TypeError, ArgumentError
begin
tm = DateTime.rfc2822(retry_after)
seconds = (tm.to_time - DateTime.now.to_time).to_i
[seconds, 0].max
rescue ArgumentError
raise Puppet::HTTP::ProtocolError, _("Failed to parse Retry-After header '%{retry_after}' as an integer or RFC 2822 date") % { retry_after: retry_after }
end
end
end
View on GitHub (pinned to e227c27540)
Solutions
- Change the origin to send delta-seconds (Retry-After: 30) or an IMF-fixdate (Retry-After: Wed, 21 Aug 2026 07:28:00 GMT).
- If a middlebox rewrites the header, disable or reformat the rewrite in the proxy config.
- If you control neither side, terminate the retry storm upstream by fixing the 503/429 condition so Retry-After is never consulted.
Example fix
# upstream middleware - before headers['Retry-After'] = '2026-08-21T07:28:00Z' # after headers['Retry-After'] = '30'
Defensive patterns
Strategy: try-catch
Try / catch
begin
client.get(uri)
rescue Puppet::HTTP::ProtocolError => e
raise unless e.message.include?('Retry-After')
# upstream sent a non-standard header; treat as transient backoff
sleep 30
retry
end Prevention
- Emit Retry-After as delta-seconds or an RFC 2822/IMF-fixdate, never ISO 8601 or duration strings.
- Disable proxy middleware that rewrites or injects Retry-After with custom formats.
When it happens
Trigger: A 429/503 response carries Retry-After: '90s', '1m30s', or an ISO 8601 date instead of delta-seconds/IMF-fixdate; a proxy or WAF injecting a custom-format Retry-After.
Common situations: API gateways and Envoy-derivatives emitting non-standard values; upstream services that correctly use ISO 8601 per some other spec but not RFC 7231/2822; hand-rolled middleware adding the header as a duration string.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Location response header is missing
- 'put' requires a 'content-type' header
- 'post' requires a 'content-type' header
- Too many HTTP retries for %{addr}
- No content type in http response; cannot parse
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/0c70d3cb0ed59c27.
Report an issue: GitHub.