we-promise/sure · error · Provider::Mfapi::Error
API error: #{parsed['message'] || parsed['status']}
Error message
API error: #{parsed['message'] || parsed['status']} What it means
Raised by Provider::Mfapi#check_api_error! when a parsed response Hash carries status == "ERROR" or "FAIL". MFAPI signals client-side failures this way with HTTP 200 — most commonly {"status": "ERROR", "message": "..."} for a scheme code it does not recognize. The thrown message embeds the upstream message when present, otherwise just the status word. This is MFAPI's canonical 'bad request / unknown scheme' channel, so the fix is almost always on the request side, not retry logic.
Source
Thrown at app/models/provider/mfapi.rb:165
interval: 1.0,
interval_randomness: 0.5,
backoff_factor: 2,
exceptions: Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [ Faraday::ConnectionFailed ]
})
faraday.request :json
faraday.response :raise_error
faraday.headers["Accept"] = "application/json"
end
end
# throttle_request and min_request_interval provided by RateLimitable
def check_api_error!(parsed)
return unless parsed.is_a?(Hash)
if parsed["status"] == "ERROR" || parsed["status"] == "FAIL"
raise Error, "API error: #{parsed['message'] || parsed['status']}"
end
end
end
View on GitHub (pinned to e69894adb9)
Solutions
- Read the embedded message — it usually says exactly what MFAPI disliked (e.g. invalid scheme code).
- Re-resolve the symbol through search_securities and use the returned schemeCode rather than a hand-entered value.
- If the scheme was genuinely delisted/merged, update or remove the stored Security instead of retrying.
- Only retry when the message indicates a transient condition; status ERROR for unknown codes will never succeed on retry.
Example fix
# before
security = provider.fetch_security_prices(symbol: "AXIS_BLUECHIP", start_date: ..., end_date: ...)
# after
match = provider.search_securities("Axis Bluechip Direct Growth").data.first
security = provider.fetch_security_prices(symbol: match.symbol, start_date: ..., end_date: ...) Defensive patterns
Strategy: try-catch
Validate before calling
code = code.to_s.strip raise ArgumentError, "schemeCode must be numeric" unless code.match?(/\A\d+\z/)
Try / catch
begin
provider.fetch_security_prices(symbol: code, start_date: from, end_date: to)
rescue Provider::Mfapi::Error => e
raise if e.message.exclude?("API error") # only swallow upstream API errors
mark_security_stale(code, e.message)
end Prevention
- Never feed user-typed symbols directly to /mf/{code} — always resolve through search first.
- Persist the resolved schemeCode on the Security record so later syncs reuse a known-good id.
- Distinguish permanent ERROR statuses (no retry) from transient FAIL ones (retry with backoff) in your handler.
When it happens
Trigger: GET /mf/{schemeCode} with a nonexistent or malformed scheme code (the classic trigger); a search query whose shape the API rejects; occasional upstream 5xx-style FAIL statuses during MFAPI incidents; passing an ISIN or scheme name where the numeric schemeCode is expected.
Common situations: User pastes an ISIN (INF109K01Z48) or fund name instead of the numeric schemeCode (120503); stale schemeCode persisted before a fund merger/termination; fat-fingered symbol; transient MFAPI FAIL during their maintenance.
Related errors
- Unexpected response format from search endpoint
- No NAV found for scheme #{symbol} on or before #{date}
- No NAV data returned for scheme #{symbol}
- SnapTrade API error (#{operation}): HTTP #{response.status}
- API error: #{parsed["Error Message"]}
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/9909a3f158d37d49.
Report an issue: GitHub.