we-promise/sure · warning
<%= class_name %> API: #{operation_name} failed (attempt #{r
Error message
<%= class_name %> API: #{operation_name} failed (attempt #{retries}/#{max_retries}): #{e.class}: #{e.message}. Retrying in #{delay}s... What it means
Inside the generated provider SDK's with_retries wrapper: a call that raised one of RETRYABLE_ERRORS is about to be retried. The warning reports attempt N of max_retries, the exception class/message, and the computed backoff delay, then sleeps and retries the yield. This message is transient by design; the terminal condition is the sibling error log "failed after N retries" followed by raising Error with :network_error after exhaustion.
Source
Thrown at lib/generators/provider/family/templates/provider_sdk.rb.tt:144
INITIAL_RETRY_DELAY = 2 # seconds
def validate_configuration!
<% secret_fields.each do |field| -%>
raise ConfigurationError, "<%= field[:name].humanize %> is required" if @<%= field[:name] %>.blank?
<% end -%>
end
def with_retries(operation_name, max_retries: MAX_RETRIES)
retries = 0
begin
yield
rescue *RETRYABLE_ERRORS => e
retries += 1
if retries <= max_retries
delay = calculate_retry_delay(retries)
Rails.logger.warn(
"<%= class_name %> API: #{operation_name} failed (attempt #{retries}/#{max_retries}): " \
"#{e.class}: #{e.message}. Retrying in #{delay}s..."
)
sleep(delay)
retry
else
Rails.logger.error(
"<%= class_name %> API: #{operation_name} failed after #{max_retries} retries: " \
"#{e.class}: #{e.message}"
)
raise Error.new("Network error after #{max_retries} retries: #{e.message}", :network_error)
end
end
end
def calculate_retry_delay(retry_count)
base_delay = INITIAL_RETRY_DELAY * (2 ** (retry_count - 1))
jitter = base_delay * rand * 0.25View on GitHub (pinned to e69894adb9)
Solutions
- Check whether the follow-up 'failed after N retries' error line appears — if yes, treat it as a provider outage/rate limit and investigate that; if no, the retry succeeded and no action is needed
- Inspect the exception class in the message: ECONNRESET/timeouts suggest network or provider health; rate-limit errors mean tuning backoff or honoring Retry-After
- Tune max_retries and calculate_retry_delay (add jitter) in the generated SDK so retries do not hammer a recovering provider
- For persistent issues, verify the API base URL and credentials, and check the provider status page
Example fix
# generated provider_sdk.rb - before delay = calculate_retry_delay(retries) sleep(delay) # after (full jitter to avoid thundering herd) delay = calculate_retry_delay(retries) sleep(rand * delay)
Defensive patterns
Strategy: retry
Validate before calling
# Pre-flight the provider endpoint before batching many SDK calls require "socket" def provider_reachable?(host, port = 443, timeout = 2) Socket.tcp(host, port, connect_timeout: timeout).close true rescue StandardError false end
Type guard
def retryable?(exception)
RETRYABLE_ERRORS.any? { |klass| exception.is_a?(klass) }
end Try / catch
# Keep retry semantics but classify outcomes explicitly
rescue *RETRYABLE_ERRORS => e
retries += 1
if retries <= max_retries
delay = calculate_retry_delay(retries)
Rails.logger.warn("... Retrying in #{delay}s...")
sleep(rand * delay) # jitter
retry
else
raise Error.new("Network error after #{max_retries} retries: #{e.message}", :network_error)
end
end Prevention
- Add jitter to calculate_retry_delay so concurrent jobs don't retry in lockstep during provider outages
- Distinguish rate-limit responses from network faults and honor Retry-After when the provider sends it
- Cap total sleep time and push long retries to Sidekiq's own retry mechanism instead of blocking worker threads
When it happens
Trigger: Any RETRYABLE_ERRORS member raised by the wrapped SDK call: connection resets (Errno::ECONNRESET), timeouts (Net::OpenTimeout/ReadTimeout), 5xx-driven exceptions depending on the constant's contents — each occurrence during a single operation increments the attempt counter and logs this line (provider_sdk.rb.tt:144 region).
Common situations: Provider API outage or degraded performance; rate limiting that surfaces as timeouts; restrictive egress rules/proxies causing intermittent resets; retry storms when many jobs retry simultaneously during provider downtime because sleep blocks the worker thread.
Related errors
- External assistant is temporarily unavailable.
- <%= class_name %>ConnectionCleanupJob - Failed: #{e.class} -
- <%= class_name %>ConnectionCleanupJob - API delete failed: #
- Lunchflow provider is not configured
- Please provide a name and a complete US address (street, cit
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/de0cbe82ba7e610a.
Report an issue: GitHub.