we-promise/sure · error · Error
network_error
network_error
Error message
Network error after #{max_retries} retries: #{e.message} What it means
IndexaCapital's with_retries exhausted MAX_RETRIES=3 attempts with exponential backoff (2s base, cap 30s, jitter) on a transport error (SocketError, Net timeouts, ECONNRESET/REFUSED/ETIMEDOUT, EOFError) against https://api.indexacapital.com. It wraps any of the four public operations (list_accounts, get_holdings, get_portfolio, get_account_balance) and re-raises as Error(:network_error).
Source
Thrown at app/models/provider/indexa_capital.rb:145
begin
yield
rescue *RETRYABLE_ERRORS => e
retries += 1
if retries <= max_retries
delay = calculate_retry_delay(retries)
Rails.logger.warn(
"IndexaCapital API: #{operation_name} failed (attempt #{retries}/#{max_retries}): " \
"#{e.class}: #{e.message}. Retrying in #{delay}s..."
)
sleep(delay)
retry
else
Rails.logger.error(
"IndexaCapital 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
[ base_delay + jitter, 30 ].min
end
def base_url
BASE_URL
end
def base_headers
{
"Content-Type" => "application/json",
"Accept" => "application/json"View on GitHub (pinned to e69894adb9)
Solutions
- Confirm reachability from the app host: curl -v https://api.indexacapital.com/users/me (401 is fine - it proves the path works)
- Check HTTP_PROXY/HTTPS_PROXY env vars and DNS resolution in the runtime environment
- Let the scheduler retry on the next cycle; :network_error is never a credential problem, so do not lock the account
- If timeouts dominate, raise the HTTParty timeout or fetch per-account data in smaller jobs
Example fix
# before provider.get_holdings(account_number: num) # raises mid-loop, kills whole sync # after begin provider.get_holdings(account_number: num) rescue Provider::IndexaCapital::Error => e next if e.error_type == :network_error # log and skip, retry next cycle raise end
Defensive patterns
Strategy: retry
Validate before calling
require "socket"
def indexa_reachable?(timeout: 3)
TCPSocket.new("api.indexacapital.com", 443, connect_timeout: timeout).close
true
rescue SocketError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, IO::TimeoutError
false
end Type guard
def indexa_network_error?(error) error.is_a?(Provider::IndexaCapital::Error) && error.error_type == :network_error end
Try / catch
begin provider.get_portfolio(account_number: num) rescue Provider::IndexaCapital::Error => e raise unless e.error_type == :network_error skipped << account # log and continue the batch; retry next cycle end
Prevention
- Allowlist api.indexacapital.com:443 on app hosts
- Skip-and-record on :network_error in batch loops so one host-level outage doesn't kill the run
- Never lock credentials or the account on :network_error - it is transport, not auth
- Prefer fresh provider instances per run so stale memoized state can't compound
When it happens
Trigger: DNS failure resolving api.indexacapital.com; firewall blocking egress to Indexa; repeated ReadTimeouts on /accounts/{n}/portfolio for large portfolios; a proxy resetting connections from the app host.
Common situations: Containerized deploys missing allowlist entries, transient Spanish-provider network issues outlasting the ~6s of cumulative backoff, local dev behind a VPN/proxy that resets long-lived TLS sessions.
Related errors
- Network error after #{max_retries} retries: #{e.message}
- network_error
- Lunchflow provider is not configured
- request_failed
- request_failed
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/b8e80486c69888e3.
Report an issue: GitHub.