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.25

View on GitHub (pinned to e69894adb9)

Solutions

  1. 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
  2. Inspect the exception class in the message: ECONNRESET/timeouts suggest network or provider health; rate-limit errors mean tuning backoff or honoring Retry-After
  3. Tune max_retries and calculate_retry_delay (add jitter) in the generated SDK so retries do not hammer a recovering provider
  4. 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

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


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/de0cbe82ba7e610a. Report an issue: GitHub.