{"record":{"id":"4a724c4d1d92f2f4","repo":"instructure/canvas-lms","slug":"unsupported-grant-type","errorCode":"unsupported_grant_type","errorMessage":"unsupported_grant_type","messagePattern":"unsupported_grant_type","errorType":"error_code","errorClass":"Canvas::OAuth::RequestError","httpStatus":400,"severity":"error","filePath":"app/controllers/oauth2_provider_controller.rb","lineNumber":203,"sourceCode":"                if Canvas::OAuth::PKCE.use_pkce_in_token?(params)\n                  Canvas::OAuth::GrantTypes::AuthorizationCodeWithPKCE.new(client_id, secret, params)\n                else\n                  Canvas::OAuth::GrantTypes::AuthorizationCode.new(client_id, secret, params)\n                end\n              when \"refresh_token\"\n                Canvas::OAuth::GrantTypes::RefreshToken.new(client_id, secret, params)\n              when \"client_credentials\"\n                Canvas::OAuth::GrantTypes::ClientCredentials.new(\n                  params,\n                  request.host_with_port,\n                  @domain_root_account,\n                  request.protocol\n                )\n              else\n                Canvas::OAuth::GrantTypes::BaseType.new(client_id, secret, params)\n              end\n\n    raise Canvas::OAuth::RequestError, :unsupported_grant_type unless granter.supported_type?\n\n    token = granter.token\n    # make sure locales are set up\n    if token.is_a?(Canvas::OAuth::Token)\n      @current_user = token.user\n      assign_localizer\n      I18n.set_locale_with_localizer\n    end\n\n    render json: token\n  end\n\n  def destroy\n    if params[:expire_sessions]\n      if session[:login_aac]\n        # The AAC could have been deleted since the user logged in\n        @aac = AuthenticationProvider.where(id: session[:login_aac]).first\n        redirect = @aac.try(:user_logout_redirect, self, @current_user)","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/controllers/oauth2_provider_controller.rb#L185-L221","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\ncurl -d 'client_id=..&client_secret=..&code=abc' https://canvas/login/oauth2/token\n# after\ncurl -d 'grant_type=authorization_code&client_id=..&client_secret=..&code=abc' https://canvas/login/oauth2/token","handlingStrategy":"validation","validationCode":"const SUPPORTED_GRANTS = ['authorization_code', 'refresh_token', 'client_credentials'];\nif (!SUPPORTED_GRANTS.includes(grantType)) throw new Error(`grant_type '${grantType}' unsupported by Canvas OAuth2 token endpoint`);","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["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"],"tags":["oauth2","token-exchange","grant-type","canvas-lms"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}