{"record":{"id":"e3704b87d0b0d32b","repo":"we-promise/sure","slug":"request-failed-e3704b","errorCode":"request_failed","errorMessage":"Sophtron request failed: #{e.message}","messagePattern":"Sophtron request failed: #(.+?)","errorType":"exception","errorClass":"Provider::Sophtron::Error","httpStatus":null,"severity":"error","filePath":"app/models/provider/sophtron.rb","lineNumber":335,"sourceCode":"        parsed = parsed.with_indifferent_access\n        keys.each do |key|\n          return Array(parsed[key]) if parsed.key?(key)\n        end\n      end\n\n      raise Error.new(\"Invalid Sophtron response format\", :invalid_response, details: parsed)\n    end\n\n    def request(method, api_path, body: nil, parse_json: true)\n      options = { headers: auth_headers(method: method, api_path: api_path) }\n      options[:body] = body.to_json if body\n\n      response = self.class.public_send(method, \"#{base_url}#{api_path}\", options)\n      handle_response(response, parse_json: parse_json)\n    rescue Error\n      raise\n    rescue SocketError, Net::OpenTimeout, Net::ReadTimeout => e\n      raise Error.new(\"Sophtron request failed: #{e.message}\", :request_failed)\n    rescue StandardError => e\n      raise Error.new(\"Sophtron request failed: #{e.message}\", :request_failed)\n    end\n\n    def auth_headers(method:, api_path:)\n      {\n        \"Authorization\" => auth_header_for(method, api_path),\n        \"Content-Type\" => \"application/json\",\n        \"Accept\" => \"application/json\"\n      }\n    end\n\n    def handle_response(response, parse_json: true)\n      body = response.body.to_s\n\n      case response.code.to_i\n      when 200, 201, 204\n        return {} if body.strip.blank?","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/sophtron.rb#L317-L353","documentation":"Raised by Provider::Sophtron#request when the HTTP transport itself fails with SocketError, Net::OpenTimeout, or Net::ReadTimeout (HTTParty under the hood, with a 120s timeout and verified TLS). The request never completed - no status code exists. Distinguished from 137, which is the generic StandardError rescue below it.","triggerScenarios":"DNS resolution failure for the Sophtron base_url (SocketError), connection not established within the open timeout, or the server accepted the connection but the body never arrived within the 120s read timeout (large account/transaction exports on a slow upstream).","commonSituations":"Typo'd or unreachable SOPHTRON_BASE_URL override; Sophtron API outage; app host DNS broken in Docker/Kubernetes; very large responses (years of transactions) exceeding even the generous read timeout on slow days.","solutions":["Resolve/verify the base_url from the app host: curl -v <base_url>/health - SocketError means DNS/name resolution trouble","Retry the operation - timeouts during heavy exports are often transient load, not config","For chronic read timeouts on big exports, fetch in smaller date windows rather than raising the 120s ceiling","Check whether a custom base_url was configured (normalize_base_url applied) and correct it to Sophtron's documented host"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"require \"resolv\"\nbegin\n  Resolv::DNS.open { |d| d.getresources(URI.parse(base_url).host, Resolv::DNS::Resource::IN::A) }\nrescue => e\n  raise \"Sophtron base_url not resolvable: #{base_url}\"\nend","typeGuard":null,"tryCatchPattern":"begin\n  client.request(:get, \"/Institution/allInstitutions\")\nrescue Provider::Sophtron::Error => e\n  if e.error_type == :request_failed && e.message.match?(/SocketError|Timeout|timed out/i)\n    retry_job_with_backoff # transport-level, transient\n  else\n    raise\n  end\nend","preventionTips":["Verify DNS/egress for the Sophtron base_url from the app host before wiring a new environment","Chunk large history exports into date windows so single responses stay well under the 120s read timeout","Distinguish this (no HTTP response) from 401/400 (server responded) before choosing a fix"],"tags":["sophtron","network","timeout","dns","httparty"],"backgroundTag":"connection-timeout","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}