{"record":{"id":"88528fc14477e238","repo":"puppetlabs/puppet","slug":"retries-exceeded","errorCode":null,"errorMessage":"%{retries} exceeded","messagePattern":"%(.+?) exceeded","errorType":"exception","errorClass":"Puppet::Util::RetryAction::RetryException::RetriesExceeded","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/retry_action.rb","lineNumber":33,"sourceCode":"  def self.retry_action(options = {})\n    # Retry actions for a specified amount of time. This method will allow the final\n    # retry to complete even if that extends beyond the timeout period.\n    unless block_given?\n      raise RetryException::NoBlockGiven\n    end\n\n    retries = options[:retries]\n    if retries.nil?\n      raise RetryException::NoRetriesGiven\n    end\n\n    retry_exceptions = options[:retry_exceptions] || [StandardError]\n    failures = 0\n    begin\n      yield\n    rescue *retry_exceptions => e\n      if failures >= retries\n        raise RetryException::RetriesExceeded, _(\"%{retries} exceeded\") % { retries: retries }, e.backtrace\n      end\n\n      Puppet.info(_(\"Caught exception %{klass}:%{error} retrying\") % { klass: e.class, error: e })\n\n      failures += 1\n\n      # Increase the amount of time that we sleep after every\n      # failed retry attempt.\n      sleep(((2**failures) - 1) * 0.1)\n\n      retry\n    end\n  end\nend\n","sourceCodeStart":15,"sourceCodeEnd":48,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/retry_action.rb#L15-L48","documentation":"Puppet::Util::RetryAction.retry_action (retry_action.rb:33) re-runs the block with exponential backoff (sleep ((2**failures)-1)*0.1) whenever an exception in retry_exceptions (default [StandardError]) is raised. Once the failure count reaches options[:retries], it raises RetryException::RetriesExceeded with '<retries> exceeded', carrying the original exception's backtrace. Note RetryException inherits from Exception, NOT StandardError, so a bare 'rescue => e' will not catch it.","triggerScenarios":"retry_action(retries: 3) { flaky_network_call } where the call keeps raising StandardError subclasses; retries: 0 combined with any single failure raises immediately; wrapping service/DB start loops whose downtime exceeds 3-4 backoff cycles (0.1s, 0.3s, 0.7s, 1.5s...).","commonSituations":"Waiting for a database or HTTP service to become reachable during provisioning; retrying flaky external APIs with a too-small :retries; retry-exceptions misconfigured so an unexpected error class is counted.","solutions":["Fix or verify the underlying cause first: the original exception is logged via Puppet.info before the final raise and its backtrace is attached.","Raise options[:retries] to cover realistic startup/timeout windows (backoff grows roughly exponentially from 0.1s).","Narrow :retry_exceptions to the specific errors worth retrying (e.g., [Errno::ECONNREFUSED, Timeout::Error]) so unrelated bugs fail fast.","Catch Puppet::Util::RetryAction::RetryException::RetriesExceeded explicitly (it is an Exception, not StandardError) and escalate with context."],"exampleFix":"// before\nPuppet::Util::RetryAction.retry_action(retries: 2) { client.create } # raises '2 exceeded' on flaky API\n\n// after\nbegin\n  Puppet::Util::RetryAction.retry_action(retries: 6, retry_exceptions: [Errno::ECONNREFUSED, Timeout::Error]) { client.create }\nrescue Puppet::Util::RetryAction::RetryException::RetriesExceeded => e\n  raise \"service still unreachable after backoff: #{e.message}\"\nend","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  Puppet::Util::RetryAction.retry_action(retries: retries, retry_exceptions: retryable) { yield }\nrescue Puppet::Util::RetryAction::RetryException::RetriesExceeded => e\n  raise \"gave up after #{retries} retries: #{e.message}\" # inherits Exception; a bare rescue will NOT catch it\nend","preventionTips":["Size :retries against the real startup time of the dependency; backoff is only 0.1s*(2^n-1).","Restrict :retry_exceptions to genuinely transient errors so real bugs surface on first failure.","Always rescue RetryException::RetriesExceeded explicitly; it descends from Exception, not StandardError."],"tags":["puppet","retry","exponential-backoff","resilience","timeout"],"backgroundTag":"retry-limit-exhausted","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}