{"record":{"id":"809a34ac7ff230a4","repo":"arsduo/koala","slug":"oauthtokenrequesterror-new-response-status-respon","errorCode":null,"errorMessage":"OAuthTokenRequestError.new(response.status, response.body)","messagePattern":"OAuthTokenRequestError\\.new\\(response\\.status, response\\.body\\)","errorType":"http","errorClass":"Koala::Facebook::OAuthTokenRequestError","httpStatus":400,"severity":"error","filePath":"lib/koala/oauth.rb","lineNumber":312,"sourceCode":"              raise\n            end\n          end\n\n          components.merge(token_info) if token_info\n        else\n          Koala::Utils.logger.warn(\"Signed cookie didn't contain Facebook OAuth code! Components: #{components}\")\n          nil\n        end\n      end\n\n      def fetch_token_string(args, post = false, endpoint = \"access_token\", options = {})\n        response = Koala.make_request(\"/oauth/#{endpoint}\", {\n          :client_id => @app_id,\n          :client_secret => @app_secret\n        }.merge!(args), post ? \"post\" : \"get\", {:use_ssl => true}.merge!(options))\n\n        raise ServerError.new(response.status, response.body) if response.status >= 500\n        raise OAuthTokenRequestError.new(response.status, response.body) if response.status >= 400\n\n        response.body\n      end\n\n      # base 64\n      # directly from https://github.com/facebook/crypto-request-examples/raw/master/sample.rb\n      def base64_url_decode(str)\n        str += '=' * (4 - str.length.modulo(4))\n        Base64.decode64(str.tr('-_', '+/'))\n      end\n\n      def server_url(type)\n        url = \"https://#{Koala.config.send(type)}\"\n        if version = Koala.config.api_version\n          \"#{url}/#{version}\"\n        else\n          url\n        end","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/arsduo/koala/blob/47d052063ef8b5644fb59e279da0b52687999f55/lib/koala/oauth.rb#L294-L330","documentation":"When a call to an /oauth endpoint (code redemption, app token, token exchange, client code) returns any 4xx status, fetch_token_string raises Koala::Facebook::OAuthTokenRequestError with http_status and response_body attached; APIError parsing fills in fb_error_code, fb_error_subcode, and fb_error_message from the Facebook JSON error. The meaning: Facebook understood the request and refused it (invalid or expired code, redirect_uri mismatch, wrong client credentials), so retrying the identical call will not help.","triggerScenarios":"get_access_token(code) with a code that was already redeemed (codes are single-use; the get_user_info_from_cookies docs warn the method can be called once per session), a code older than roughly ten minutes, a redirect_uri at redemption that differs from the one used in url_for_oauth_code, or a client_id and client_secret that do not match the app that issued the code. parse_signed_cookie internally rescues this error for the Code was invalid or expired case and returns nil; other paths surface it directly.","commonSituations":"Double redemption: the server-side flow and the Facebook JS SDK both consuming the same code, or two requests racing on the same cookie; a dev redirect URI used in production; users sitting on the consent screen until the code expires; page reload re-posting the callback URL.","solutions":["Redeem each code exactly once and persist the resulting token; make the callback idempotent (for example session[:token] ||= exchange).","Keep the redirect_uri byte-identical between url_for_oauth_code and get_access_token; get_access_token_info defaults to @oauth_callback_url for both, so configure one source of truth.","Inspect e.fb_error_message and e.fb_error_code: expired or already-used codes call for restarting the flow, parameter errors for fixing the request.","On expiry, send the user back through url_for_oauth_code instead of retrying the dead code."],"exampleFix":"// before\ndef callback\n  session[:token] = @oauth.get_access_token(params[:code])\nend\n\n// after\ndef callback\n  session[:token] ||= @oauth.get_access_token(params[:code])\nrescue Koala::Facebook::OAuthTokenRequestError => e\n  if e.fb_error_message.to_s.match?(/expired|invalid|been used/i)\n    redirect_to @oauth.url_for_oauth_code(redirect_uri: callback_url)\n  else\n    raise\n  end\nend","handlingStrategy":"try-catch","validationCode":"return if params[:code].blank? || session[:token] # a code is needed only once","typeGuard":"def fresh_code?(code)\n  code.is_a?(String) && code.length > 20 && !session.key?(:token)\nend","tryCatchPattern":"begin\n  @oauth.get_access_token_info(code)\nrescue Koala::Facebook::OAuthTokenRequestError => e\n  if e.fb_error_message.to_s.match?(/expired|been used|invalid/i)\n    restart_oauth_flow\n  else\n    raise\n  end\nend","preventionTips":["Persist the token on first redemption and make the OAuth callback idempotent under reload","Use exactly one redemption path per login (JS SDK cookie or server-side code, not both)","Store the redirect_uri next to authorization URL construction so redemption matches by construction"],"tags":["oauth","http-4xx","authorization-code","access-token","koala","facebook"],"backgroundTag":"oauth-token-request-failed","analyzedSha":"47d052063ef8b5644fb59e279da0b52687999f55","analyzedAt":"2026-08-23T10:19:03.891Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}