{"record":{"id":"e46f1b80eaf9f4e8","repo":"we-promise/sure","slug":"request-failed-e46f1b","errorCode":"request_failed","errorMessage":"Exception during #{operation_name}: #{e.message}","messagePattern":"Exception during #(.+?): #(.+?)","errorType":"exception","errorClass":"Provider::Simplefin::SimplefinError","httpStatus":null,"severity":"error","filePath":"app/models/provider/simplefin.rb","lineNumber":165,"sourceCode":"          sleep(delay) if backoff && delay.to_f.positive?\n          retry\n        else\n          Rails.logger.error(\n            \"SimpleFin API: #{operation_name} failed after #{max_retries} retries: \" \\\n            \"#{e.class}: #{e.message}\"\n          )\n          raise SimplefinError.new(\n            \"Network error after #{max_retries} retries: #{e.message}\",\n            :network_error\n          )\n        end\n      rescue SimplefinError => e\n        # Preserve original error type and message.\n        raise\n      rescue => e\n        # Non-retryable errors are logged and re-raised immediately\n        Rails.logger.error \"SimpleFin API: #{operation_name} failed with non-retryable error: #{e.class}: #{e.message}\"\n        raise SimplefinError.new(\"Exception during #{operation_name}: #{e.message}\", :request_failed)\n      end\n    end\n\n    # Calculate delay with exponential backoff and jitter\n    def calculate_retry_delay(retry_count)\n      # Exponential backoff: 2^retry * initial_delay\n      base_delay = INITIAL_RETRY_DELAY * (2 ** (retry_count - 1))\n      # Add jitter (0-25% of base delay) to prevent thundering herd\n      jitter = base_delay * rand * 0.25\n      # Cap at max delay\n      [ base_delay + jitter, MAX_RETRY_DELAY ].min\n    end\nend\n","sourceCodeStart":147,"sourceCodeEnd":179,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/simplefin.rb#L147-L179","documentation":"The catch-all rescue in Provider::Simplefin.with_retries: any exception that is not a SimplefinError and not a retryable network error is logged as 'non-retryable' and re-raised wrapped in SimplefinError(:request_failed). The original class name is only in the log line, not in the message, so the Rails log is the key diagnostic.","triggerScenarios":"Non-network failures inside the HTTP operation: JSON::ParserError while parsing a 2xx body, OpenSSL::SSL::SSLError from a bad TLS handshake/cert, TypeError/ArgumentError from a malformed base_url (e.g. access URL stored with spaces), or any unexpected bug in the request block.","commonSituations":"Access URL saved with a typo that survives URI parsing but breaks later; a TLS-intercepting corporate proxy presenting an untrusted cert; SimpleFIN returning HTML instead of JSON on a 200; code changes introducing a NoMethodError inside the block.","solutions":["Grep the log for 'failed with non-retryable error' - the e.class recorded there (e.g. OpenSSL::SSL::SSLError vs JSON::ParserError) identifies the family","For SSL errors, verify the cert chain with curl -v and check whether a proxy intercepts TLS; do not disable verification","For parse/type errors, reproduce the raw request with curl and inspect the actual response body/content-type","Fix the root cause rather than retrying - this wrapper deliberately does not retry because these errors are deterministic"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  simplefin.get_accounts\nrescue Provider::Simplefin::SimplefinError => e\n  if e.error_type == :request_failed\n    report_with_context(e.message) # deterministic bug or env issue - do not retry\n  else\n    raise\n  end\nend","preventionTips":["Retrying this class of error is wasted work - it wraps deterministic failures","Capture the Rails log line with the original e.class when reporting","Smoke-test credential URLs and TLS on new deploy environments before enabling syncs"],"tags":["simplefin","wrapped-exception","non-retryable","catch-all"],"backgroundTag":"unexpected-exception-wrapped","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}