{"record":{"id":"12fed8bed69c61b4","repo":"we-promise/sure","slug":"no-nav-data-returned-for-scheme-symbol","errorCode":null,"errorMessage":"No NAV data returned for scheme #{symbol}","messagePattern":"No NAV data returned for scheme #(.+?)","errorType":"exception","errorClass":"Provider::Mfapi::InvalidSecurityPriceError","httpStatus":null,"severity":"error","filePath":"app/models/provider/mfapi.rb","lineNumber":114,"sourceCode":"      historical_data.data.select { |p| p.date <= date }.max_by(&:date) || historical_data.data.first\n    end\n  end\n\n  def fetch_security_prices(symbol:, exchange_operating_mic: nil, start_date:, end_date:)\n    with_provider_response do\n      throttle_request\n      response = client.get(\"#{base_url}/mf/#{CGI.escape(symbol)}\") do |req|\n        req.params[\"startDate\"] = start_date.to_s\n        req.params[\"endDate\"] = end_date.to_s\n      end\n\n      parsed = JSON.parse(response.body)\n      check_api_error!(parsed)\n\n      nav_data = parsed[\"data\"]\n\n      if nav_data.nil? || !nav_data.is_a?(Array)\n        raise InvalidSecurityPriceError, \"No NAV data returned for scheme #{symbol}\"\n      end\n\n      nav_data.filter_map do |entry|\n        nav = entry[\"nav\"]\n        date_str = entry[\"date\"]\n\n        next if nav.nil? || nav.to_f <= 0 || date_str.blank?\n\n        # MFAPI returns dates as DD-MM-YYYY\n        date = Date.strptime(date_str, \"%d-%m-%Y\")\n\n        Price.new(\n          symbol: symbol,\n          date: date,\n          price: nav.to_f,\n          currency: \"INR\",\n          exchange_operating_mic: exchange_operating_mic\n        )","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/mfapi.rb#L96-L132","documentation":"Raised by Provider::Mfapi#fetch_security_prices (as InvalidSecurityPriceError) when the /mf/{schemeCode}?startDate=...&endDate=... response is valid JSON but its data field is either absent (nil) or not an Array. The MFAPI NAV contract is {meta: {...}, data: [{date, nav}, ...]}, so a missing/non-array data means the payload is an error or maintenance envelope that check_api_error! did not classify (it only fires on status ERROR/FAIL). Distinct from error 341: here the payload shape itself is wrong, before any row filtering happens.","triggerScenarios":"MFAPI returns {\"message\": \"...\"} or {} for an overloaded/rate-limited request; a scheme that exists but returns an object without data while its status field is missing; upstream schema change renaming data; intermediate proxy returning valid-JSON error object without status.","commonSituations":"Hitting MFAPI's unwritten rate limits during a large portfolio backfill; MFAPI maintenance windows; using a schemeCode that triggers a non-standard response (e.g. non-numeric garbage in the URL segment, though that usually yields status ERROR); CDN edge responses.","solutions":["Capture and inspect response.body for the failing schemeCode to see which envelope MFAPI actually returned.","Treat a missing data key as an upstream error: log it via DebugLogEntry with provider_key and retry once after backoff before raising.","Confirm the schemeCode exists via the /mf/search endpoint first when the symbol is user-supplied.","If MFAPI renamed the field, map the new key with parsed[\"data\"] || parsed[\"nav\"] fallback only after verifying upstream docs."],"exampleFix":"# before\nnav_data = parsed[\"data\"]\nif nav_data.nil? || !nav_data.is_a?(Array)\n  raise InvalidSecurityPriceError, \"No NAV data returned for scheme #{symbol}\"\nend\n\n# after\nnav_data = parsed.is_a?(Hash) ? parsed[\"data\"] : nil\nif nav_data.nil? || !nav_data.is_a?(Array)\n  DebugLogEntry.capture(\"mfapi\", :error, \"Unexpected NAV payload for #{symbol}\", metadata: { body: parsed.to_s.truncate(500) })\n  raise InvalidSecurityPriceError, \"No NAV data returned for scheme #{symbol}\"\nend","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  series = provider.fetch_security_prices(symbol: code, start_date: from, end_date: to)\nrescue Provider::Mfapi::InvalidSecurityPriceError => e\n  DebugLogEntry.capture(\"mfapi\", :error, e.message, provider_key: \"mfapi\")\n  series = ProviderResponse.new(data: [])\nend","preventionTips":["Log the raw payload whenever the data key is missing — envelope drift must be seen to be fixed.","Honor the provider's throttle_request interval in bulk backfills to avoid rate-limit envelopes.","Contract-test the /mf/{code} response shape (meta + data array) in CI."],"tags":["mfapi","mutual-funds","nav","api-contract","response-shape"],"backgroundTag":"unexpected-api-response-format","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}