we-promise/sure · error · Error
bad_response
bad_response
Error message
Invalid JSON in response: #{e.message} What it means
A 200/201 response from api.indexacapital.com whose body is not valid JSON (JSON::ParserError), re-raised as Error(:bad_response) with the parser's message embedded. The raw body itself is not included in the error - only e.message's parser detail - so diagnosis leans on the message shape ('unexpected token at ...' usually means HTML).
Source
Thrown at app/models/provider/indexa_capital.rb:198
username: username,
document: document,
password: password
}.to_json
)
payload = handle_response(response)
jwt = payload[:token]
raise AuthenticationError.new("Authentication token missing in response", :unauthorized) if jwt.blank?
jwt
end
def handle_response(response)
case response.code
when 200, 201
begin
JSON.parse(response.body, symbolize_names: true)
rescue JSON::ParserError => e
raise Error.new("Invalid JSON in response: #{e.message}", :bad_response)
end
when 400
Rails.logger.error "IndexaCapital API: Bad request - #{response.body}"
raise Error.new("Bad request: #{response.body}", :bad_request)
when 401
raise AuthenticationError.new("Invalid credentials", :unauthorized)
when 403
raise AuthenticationError.new("Access forbidden - check your permissions", :access_forbidden)
when 404
raise Error.new("Resource not found", :not_found)
when 429
raise Error.new("Rate limit exceeded. Please try again later.", :rate_limited)
when 500..599
raise Error.new("IndexaCapital server error (#{response.code}). Please try again later.", :server_error)
else
Rails.logger.error "IndexaCapital API: Unexpected response - Code: #{response.code}, Body: #{response.body}"
raise Error.new("Unexpected error: #{response.code} - #{response.body}", :unknown)
endView on GitHub (pinned to e69894adb9)
Solutions
- Match on the parser detail in e.message: 'unexpected token at \'<\'' almost always means an HTML page came back
- Retry once after a short delay - interstitials are typically one-shot
- Add the raw body to the error message (it is currently dropped) and capture it via DebugLogEntry for support
- Verify no intermediary (proxy, SSL inspection) sits between the app and api.indexacapital.com
Example fix
# app/models/provider/indexa_capital.rb - handle_response
# before
rescue JSON::ParserError => e
raise Error.new("Invalid JSON in response: #{e.message}", :bad_response)
# after
rescue JSON::ParserError => e
raise Error.new("Invalid JSON in response: #{e.message} - body: #{response.body.to_s[0, 200]}", :bad_response) Defensive patterns
Strategy: try-catch
Type guard
def indexa_bad_response?(error) error.is_a?(Provider::IndexaCapital::Error) && error.error_type == :bad_response end
Try / catch
begin
provider.list_accounts
rescue Provider::IndexaCapital::Error => e
raise unless e.error_type == :bad_response
retry if (retries += 1) <= 1 && e.message.include?("<") # HTML interstitial: one retry
raise
end Prevention
- Treat 'unexpected token at <' in the message as an HTML page, not bad data
- Patch the provider to embed the raw body (first ~200 chars) in the error for diagnosability
- Retry once - interstitials and truncation are usually single-shot
- Keep intermediary proxies/SSL-inspection off the path to api.indexacapital.com
When it happens
Trigger: A WAF/CDN in front of the API returns a 200 HTML challenge or banner page; the connection drops mid-body leaving truncated JSON; an SSL-inspection proxy rewrites responses; a maintenance interstitial served with 200.
Common situations: Bot-challenge pages hitting datacenter IPs, corporate proxies injecting HTML notices, flaky networking truncating large /portfolio responses.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- parse_error
- Could not save that passkey or security key. Please try agai
- bad_request
- network_error
- unauthorized
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/c43132164585ea40.
Report an issue: GitHub.