instructure/canvas-lms · error · Canvas::OAuth::InvalidScopeError
@provider.missing_scopes
Error message
@provider.missing_scopes
What it means
Raised as Canvas::OAuth::InvalidScopeError carrying @provider.missing_scopes when the client authenticated successfully for a client_credentials grant but requested scopes the developer key is not allowed to grant (valid_scopes? false). Canvas refuses to mint a token with scopes beyond the key's configured allowlist.
Solutions
- Add the missing scopes listed in the error to the developer key's allowed scopes in Canvas admin, then retry.
- Trim the requested scope parameter to only scopes the key already grants.
- Fix scope formatting (Canvas scopes look like url:GET|/api/v1/courses) — check for typos or wrong HTTP verbs.
- If the key was recently edited, wait for/clear the key cache so valid_scopes? sees the new config.
Example fix
// before scope = 'url:GET|/api/v1/users url:POST|/api/v1/accounts/1/sub_accounts' // after (only scopes granted to the key) scope = 'url:GET|/api/v1/users'
Defensive patterns
Strategy: validation
Validate before calling
requested = opts[:scope].to_s.split granted = developer_key.scopes missing = requested - granted raise 'requesting scopes the key does not grant' if missing.any?
Try / catch
begin
token = exchange_client_credentials(opts)
rescue Canvas::OAuth::InvalidScopeError => e
logger.error("missing scopes: #{e.message}")
end Prevention
- Keep the key's scope allowlist in sync with the scopes your code requests
- Use exact Canvas scope format url:VERB|/api/v1/path
- Diff requested vs granted scopes in CI before deploying scope changes
- Request the minimal scope set so drift is less likely
When it happens
Trigger: POST to /login/oauth2/token with grant_type=client_credentials and a scope parameter containing one or more scopes not enabled on the developer key (e.g. url:POST|/api/v1/accounts/1/* missing from the key's scopes, or a service-user scope the key lacks).
Common situations: Integration requests new API scopes after code changes without updating the key; scope string typos or wrong verb/path format; copying scope lists between accounts where keys differ; key audience changed so previously granted scopes no longer apply.
Understand the failure class
Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.
Related errors
- assertion method not supported for this grant_type
- insufficient permission
- insufficient permission
- insufficient permission
- Insufficient permissions
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/47ae775f2740156c.
Report an issue: GitHub.
Appendix: source
Thrown at lib/canvas/oauth/grant_types/client_credentials.rb:78
end
def secret_for(provider, opts)
provider.try(:secret) || opts[:client_secret]
end
def key_for(client_id)
DeveloperKey.find_cached(client_id)
rescue ::ActiveRecord::RecordNotFound
nil
end
def validate_type
unless @provider.assertion_method_permitted?
raise Canvas::OAuth::InvalidRequestError, "assertion method not supported for this grant_type"
end
raise Canvas::OAuth::InvalidRequestError, @provider.error_message unless @provider.valid?
raise Canvas::OAuth::InvalidScopeError, @provider.missing_scopes unless @provider.valid_scopes?
end
def generate_token
@provider.generate_token
end
def basic_auth?(opts)
opts[:client_assertion_type] != "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
end
def scopes_from_opts(opts)
(opts[:scope] || opts[:scopes] || "").split
end
end
end
end
View on GitHub (pinned to 1c9f0bb801)