arsduo/koala · error · Koala::Facebook::OAuthTokenRequestError
OAuthTokenRequestError.new(response.status, response.body)
Error message
OAuthTokenRequestError.new(response.status, response.body)
What it means
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.
Source
Thrown at lib/koala/oauth.rb:312
raise
end
end
components.merge(token_info) if token_info
else
Koala::Utils.logger.warn("Signed cookie didn't contain Facebook OAuth code! Components: #{components}")
nil
end
end
def fetch_token_string(args, post = false, endpoint = "access_token", options = {})
response = Koala.make_request("/oauth/#{endpoint}", {
:client_id => @app_id,
:client_secret => @app_secret
}.merge!(args), post ? "post" : "get", {:use_ssl => true}.merge!(options))
raise ServerError.new(response.status, response.body) if response.status >= 500
raise OAuthTokenRequestError.new(response.status, response.body) if response.status >= 400
response.body
end
# base 64
# directly from https://github.com/facebook/crypto-request-examples/raw/master/sample.rb
def base64_url_decode(str)
str += '=' * (4 - str.length.modulo(4))
Base64.decode64(str.tr('-_', '+/'))
end
def server_url(type)
url = "https://#{Koala.config.send(type)}"
if version = Koala.config.api_version
"#{url}/#{version}"
else
url
endView on GitHub (pinned to 47d052063e)
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.
Example fix
// before
def callback
session[:token] = @oauth.get_access_token(params[:code])
end
// after
def callback
session[:token] ||= @oauth.get_access_token(params[:code])
rescue Koala::Facebook::OAuthTokenRequestError => e
if e.fb_error_message.to_s.match?(/expired|invalid|been used/i)
redirect_to @oauth.url_for_oauth_code(redirect_uri: callback_url)
else
raise
end
end Defensive patterns
Strategy: try-catch
Validate before calling
return if params[:code].blank? || session[:token] # a code is needed only once
Type guard
def fresh_code?(code) code.is_a?(String) && code.length > 20 && !session.key?(:token) end
Try / catch
begin
@oauth.get_access_token_info(code)
rescue Koala::Facebook::OAuthTokenRequestError => e
if e.fb_error_message.to_s.match?(/expired|been used|invalid/i)
restart_oauth_flow
else
raise
end
end Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- generate_client_code received an error: empty response body
- Invalid (incomplete) signature data
- Unsupported algorithm #{envelope['algorithm']}
- Invalid signature
- Koala::Utils.logger.warn("Signed cookie didn't contain Faceb
AI-assisted analysis of arsduo/koala@47d052063e (2026-08-23).
Data as JSON: /api/errors/809a34ac7ff230a4.
Report an issue: GitHub.