we-promise/sure · error · ApiError
Network error after #{max_retries} retries: #{e.message}
Error message
Network error after #{max_retries} retries: #{e.message} What it means
with_retries ran the SendRequest or GetStatement call MAX_RETRIES=3 times with exponential backoff (2s base, doubling, capped 30s, +25% jitter) and every attempt raised a transport error from RETRYABLE_ERRORS: SocketError, Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ETIMEDOUT or EOFError against ndcdyn.interactivebrokers.com.
Source
Thrown at app/models/provider/ibkr_flex.rb:135
def with_retries(operation_name, max_retries: MAX_RETRIES)
retries = 0
begin
yield
rescue *RETRYABLE_ERRORS => e
retries += 1
if retries <= max_retries
delay = calculate_retry_delay(retries)
Rails.logger.warn(
"IBKR Flex: #{operation_name} failed (attempt #{retries}/#{max_retries}): #{e.class}: #{e.message}. Retrying in #{delay}s..."
)
sleep(delay)
retry
end
raise ApiError.new("Network error after #{max_retries} retries: #{e.message}")
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, MAX_RETRY_DELAY ].min
end
end
View on GitHub (pinned to e69894adb9)
Solutions
- Verify connectivity from the same host: curl -v https://ndcdyn.interactivebrokers.com/AccountManagement/FlexWebService/SendRequest - a TLS handshake proves DNS, routing and firewall are fine
- Check DNS and proxy settings for the process (resolv.conf, HTTP_PROXY/http_proxy env leaking into HTTParty)
- Reschedule the sync - this error means the transport layer is down, so let the scheduler retry on the next cycle instead of retrying inline
- If only ReadTimeouts, the statement may be genuinely huge; shrink the Flex Query date range
Example fix
# before flex.download_statement rescue nil # silent drop # after begin flex.download_statement rescue Provider::IbkrFlex::ApiError => e SyncFailure.record!(account, provider: :ibkr_flex, reason: e.message, retry_at: 1.hour.from_now) end
Defensive patterns
Strategy: retry
Validate before calling
# cheap pre-flight before a sync run (fail fast instead of ~14s of provider backoff)
require "socket"
def ibkr_reachable?(timeout: 3)
TCPSocket.new("ndcdyn.interactivebrokers.com", 443, connect_timeout: timeout).close
true
rescue SocketError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, IO::TimeoutError
false
end Type guard
def ibkr_network_exhausted?(error)
error.is_a?(Provider::IbkrFlex::ApiError) && error.message.start_with?("Network error after")
end Try / catch
begin
flex.download_statement
rescue Provider::IbkrFlex::ApiError => e
raise unless e.message.start_with?("Network error after")
SyncFailure.record!(account, provider: :ibkr_flex,
reason: e.message, retry_at: 1.hour.from_now) # transport down; try next cycle
end Prevention
- Allowlist ndcdyn.interactivebrokers.com:443 in egress firewalls for every app host
- Add a connectivity pre-flight in deploy checks so config drift is caught early
- Hand network-exhausted errors to the scheduler instead of inline retry loops (the provider already retried 3x)
- Watch for proxy env vars (HTTP_PROXY) leaking into HTTParty
When it happens
Trigger: DNS cannot resolve ndcdyn.interactivebrokers.com; egress firewall/proxy blocks HTTPS to IBKR; connection resets from an intermediary (SSL inspection); a single GetStatement hanging past the 120s HTTParty timeout three times in a row; local network outage during a sync job.
Common situations: Corporate/容器ized environments without outbound allowlist entries for IBKR, transient ISP or IBKR edge failures lasting longer than ~14s of cumulative backoff, runs scheduled during IBKR maintenance windows.
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/01332bd39b66a16e.
Report an issue: GitHub.