we-promise/sure · error · Provider::Sophtron::Error
request_failed
request_failed
Error message
Sophtron request failed: #{e.message} What it means
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.
Source
Thrown at app/models/provider/sophtron.rb:335
parsed = parsed.with_indifferent_access
keys.each do |key|
return Array(parsed[key]) if parsed.key?(key)
end
end
raise Error.new("Invalid Sophtron response format", :invalid_response, details: parsed)
end
def request(method, api_path, body: nil, parse_json: true)
options = { headers: auth_headers(method: method, api_path: api_path) }
options[:body] = body.to_json if body
response = self.class.public_send(method, "#{base_url}#{api_path}", options)
handle_response(response, parse_json: parse_json)
rescue Error
raise
rescue SocketError, Net::OpenTimeout, Net::ReadTimeout => e
raise Error.new("Sophtron request failed: #{e.message}", :request_failed)
rescue StandardError => e
raise Error.new("Sophtron request failed: #{e.message}", :request_failed)
end
def auth_headers(method:, api_path:)
{
"Authorization" => auth_header_for(method, api_path),
"Content-Type" => "application/json",
"Accept" => "application/json"
}
end
def handle_response(response, parse_json: true)
body = response.body.to_s
case response.code.to_i
when 200, 201, 204
return {} if body.strip.blank?View on GitHub (pinned to e69894adb9)
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
Defensive patterns
Strategy: retry
Validate before calling
require "resolv"
begin
Resolv::DNS.open { |d| d.getresources(URI.parse(base_url).host, Resolv::DNS::Resource::IN::A) }
rescue => e
raise "Sophtron base_url not resolvable: #{base_url}"
end Try / catch
begin
client.request(:get, "/Institution/allInstitutions")
rescue Provider::Sophtron::Error => e
if e.error_type == :request_failed && e.message.match?(/SocketError|Timeout|timed out/i)
retry_job_with_backoff # transport-level, transient
else
raise
end
end Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/e3704b87d0b0d32b.
Report an issue: GitHub.