{"record":{"id":"f260284dc56b7ad0","repo":"we-promise/sure","slug":"failed-to-obtain-yahoo-finance-crumb","errorCode":null,"errorMessage":"Failed to obtain Yahoo Finance crumb","messagePattern":"Failed to obtain Yahoo Finance crumb","errorType":"exception","errorClass":"Provider::YahooFinance::AuthenticationError","httpStatus":null,"severity":"error","filePath":"app/models/provider/yahoo_finance.rb","lineNumber":910,"sourceCode":"          details: { status: cookie_response.status }\n        )\n      end\n\n      cookie = extract_cookie(cookie_response)\n      cookie_max_age = extract_cookie_max_age(cookie_response)\n      raise AuthenticationError, \"Failed to obtain Yahoo Finance cookie\" if cookie.blank?\n\n      crumb_response = authentication_client.get(\"#{base_url}/v1/test/getcrumb\") do |req|\n        req.headers[\"Cookie\"] = cookie\n      end\n      if crumb_response.status == 429\n        raise RateLimitError.new(\n          \"Yahoo Finance rate limit exceeded\",\n          details: { status: crumb_response.status }\n        )\n      end\n      unless crumb_response.success?\n        raise AuthenticationError.new(\n          \"Failed to obtain Yahoo Finance crumb\",\n          details: { status: crumb_response.status }\n        )\n      end\n\n      crumb = crumb_response.body.to_s.strip\n      unless valid_crumb?(crumb)\n        error_class = INVALID_CRUMBS.include?(crumb.downcase) ? RateLimitError : AuthenticationError\n        raise error_class.new(\n          \"Failed to obtain Yahoo Finance crumb\",\n          details: { status: crumb_response.status }\n        )\n      end\n\n      cache_duration = [ cookie_max_age || MAX_CRUMB_CACHE_DURATION, MAX_CRUMB_CACHE_DURATION ].min\n      [ cookie, crumb, cache_duration ]\n    end\n","sourceCodeStart":892,"sourceCodeEnd":928,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/yahoo_finance.rb#L892-L928","documentation":"Raised when Yahoo Finance's getcrumb endpoint answers non-429 non-success (e.g. 401/403/5xx), or answers 200 with a crumb that fails valid_crumb? (blank or in INVALID_CRUMBS). The class is AuthenticationError for a bad/absent crumb but RateLimitError when the body literally says 'too many requests' - Yahoo signals throttling via the crumb text.","triggerScenarios":"GET /v1/test/getcrumb returns 401/403/5xx (cookie rejected, consent not satisfied); crumb body is empty; crumb body is 'Too Many Requests' (mapped to RateLimitError via INVALID_CRUMBS).","commonSituations":"The fc.yahoo.com cookie missing required consent cookies (EU consent wall for EU egress IPs); Yahoo changing the crumb contract (new sentinel values not yet in INVALID_CRUMBS); cookie expired between the two requests; IP reputation issues making Yahoo hand out junk crumbs.","solutions":["If the error is RateLimitError (body 'Too Many Requests'), back off for minutes and redo the whole cookie+crumb handshake","If AuthenticationError: redo the handshake with a fresh cookie and verify extract_cookie pulled a real cookie (not blank)","For EU regions, ensure the handshake satisfies Yahoo's consent flow or egress from a non-consent-walled region","If a new junk crumb string appears consistently, add it to INVALID_CRUMBS and treat it as throttling"],"exampleFix":"# before\ncookie, crumb = provider.send(:request_cookie_and_crumb, client)\n\n# after - retry handshake once with fresh cookie, then give up gracefully\nbegin\n  cookie, crumb = provider.send(:request_cookie_and_crumb, client)\nrescue Provider::YahooFinance::AuthenticationError => e\n  Rails.cache.delete(\"yahoo_finance_crumb_cache\")\n  retry if (attempts += 1) == 1\n  raise\nend","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"def yahoo_auth_failed?(err)\n  err.is_a?(Provider::YahooFinance::AuthenticationError)\nend","tryCatchPattern":"attempts = 0\nbegin\n  provider.fetch_quotes(symbols)\nrescue Provider::YahooFinance::AuthenticationError, Provider::YahooFinance::RateLimitError => e\n  Rails.cache.delete(provider.class::HEALTH_STATUS_CACHE_KEY)\n  retry if (attempts += 1) == 1 # one fresh handshake\n  fall_back_to_alternate_provider(e)\nend","preventionTips":["Retry the full handshake (cookie then crumb) once before failing - half-expired pairs are common","Add newly observed junk-crumb strings to INVALID_CRUMBS so they classify as throttling","For EU egress, verify the consent cookie flow works or route via a region that skips it"],"tags":["yahoo-finance","crumb","authentication","cookie","handshake-failure"],"backgroundTag":"auth-handshake-failure","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}