instructure/canvas-lms · error · Canvas::OAuth::RequestError
unsupported_grant_type
unsupported_grant_type
Error message
unsupported_grant_type
What it means
In the OAuth2 token endpoint (POST /login/oauth2/token), Canvas selects a granter class based on the grant_type parameter; if grant_type is absent or not one of authorization_code, refresh_token, or client_credentials, the fallback BaseType granter has supported_type? == false and Canvas::OAuth::RequestError :unsupported_grant_type is raised. This matches the OAuth2 spec error of the same name.
Solutions
- Set grant_type=authorization_code (plus client_id, client_secret, and the code) for the standard code exchange
- Use grant_type=refresh_token with your stored refresh_token to renew access
- Only use grant_type=client_credentials if your key/account permits service tokens
- Print/inspect the actual POSTed body; fix typos and confirm the param is in the body (or Basic auth), not just the URL
Example fix
# before curl -d 'client_id=..&client_secret=..&code=abc' https://canvas/login/oauth2/token # after curl -d 'grant_type=authorization_code&client_id=..&client_secret=..&code=abc' https://canvas/login/oauth2/token
Defensive patterns
Strategy: validation
Validate before calling
const SUPPORTED_GRANTS = ['authorization_code', 'refresh_token', 'client_credentials'];
if (!SUPPORTED_GRANTS.includes(grantType)) throw new Error(`grant_type '${grantType}' unsupported by Canvas OAuth2 token endpoint`); Try / catch
try { token = await exchangeToken(params); } catch (e) { if (e.body?.error === 'unsupported_grant_type') { /* fix grant_type / fall back to authorization_code flow */ } throw e; } Prevention
- Always set grant_type explicitly; never rely on library defaults
- Map your flow (code exchange vs refresh) to the correct grant_type constant
- Confirm client_credentials support with your Canvas admin before using it
- Log the outgoing token-request body (minus secrets) when debugging
When it happens
Trigger: POST /login/oauth2/token with grant_type omitted, misspelled (e.g. 'authorizationcode'), or set to an unsupported type like 'password' or 'client_credentials' when the key/account disallows it.
Common situations: Sending the code exchange without grant_type=authorization_code, trying password or client_credentials grants on a key that only supports authorization code, typos after hand-writing the curl command, or a client library defaulting to a grant Canvas does not implement.
Related errors
- assertion method not supported for this grant_type
- incorrect_client
- invalid_authorization_code
- invalid_client_id
- invalid_grant
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/4a724c4d1d92f2f4.
Report an issue: GitHub.
Appendix: source
Thrown at app/controllers/oauth2_provider_controller.rb:203
if Canvas::OAuth::PKCE.use_pkce_in_token?(params)
Canvas::OAuth::GrantTypes::AuthorizationCodeWithPKCE.new(client_id, secret, params)
else
Canvas::OAuth::GrantTypes::AuthorizationCode.new(client_id, secret, params)
end
when "refresh_token"
Canvas::OAuth::GrantTypes::RefreshToken.new(client_id, secret, params)
when "client_credentials"
Canvas::OAuth::GrantTypes::ClientCredentials.new(
params,
request.host_with_port,
@domain_root_account,
request.protocol
)
else
Canvas::OAuth::GrantTypes::BaseType.new(client_id, secret, params)
end
raise Canvas::OAuth::RequestError, :unsupported_grant_type unless granter.supported_type?
token = granter.token
# make sure locales are set up
if token.is_a?(Canvas::OAuth::Token)
@current_user = token.user
assign_localizer
I18n.set_locale_with_localizer
end
render json: token
end
def destroy
if params[:expire_sessions]
if session[:login_aac]
# The AAC could have been deleted since the user logged in
@aac = AuthenticationProvider.where(id: session[:login_aac]).first
redirect = @aac.try(:user_logout_redirect, self, @current_user)View on GitHub (pinned to 1c9f0bb801)