instructure/canvas-lms · error · Canvas::OAuth::RequestError
invalid_client_id
invalid_client_id
Error message
invalid_client_id
What it means
Canvas raises Canvas::OAuth::RequestError with :invalid_client_id in the OAuth2 /login/oauth2/auth flow when the provided client_id does not correspond to a Developer Key with a valid api key (provider.has_valid_key? returns false). It mirrors the OAuth2 spec's invalid_client error for the authorization endpoint. It means the app initiating the login is not a registered/valid Canvas developer key.
Solutions
- Verify the developer key exists and is active in Canvas admin (/accounts/self/developer_keys) and copy its exact client ID (the numeric key id)
- Check that you are pointing at the correct Canvas base URL for that key (keys do not transfer across environments)
- If the key is intentionally new, activate it (and get it allow-listed on self-hosted/managed installs) before starting the auth flow
- Correct the client_id value in your OAuth config/env vars; ensure it is sent as the client_id query param on /login/oauth2/auth
Example fix
// before window.location = canvasUrl + '/login/oauth2/auth?client_id=' + process.env.CANVAS_KEY_ID_TODO // after window.location = canvasUrl + '/login/oauth2/auth?client_id=' + CANVAS_DEVELOPER_KEY_ID // verified active in Canvas admin
Defensive patterns
Strategy: validation
Validate before calling
const isValidClientId = (id) => typeof id === 'string' && /^\d{6,}$/.test(id) && Boolean(CANVAS_KEYS[id]);
if (!isValidClientId(config.clientId)) throw new Error('Canvas developer key client_id missing/invalid — check Canvas admin developer keys'); Type guard
const isRegisteredKey = (id) => typeof id === 'string' && id in registeredDeveloperKeys;
if (!isRegisteredKey(clientId)) failFast('invalid client_id before starting OAuth flow'); Prevention
- Store the client_id per environment (test/prod) in env config, never hardcode
- Assert the developer key is active during deploy/health checks
- Document who owns each dev key so deletions are announced
- Log the client_id (not the secret) on auth failures to speed triage
When it happens
Trigger: GET/POST /login/oauth2/auth with a client_id param that is missing, mistyped, points to a deleted developer key, or a key whose API key is blank/inactive.
Common situations: Using a dev key from a different Canvas environment (test vs production), a key that was deleted or deactivated by an admin, a client_id copied from docs/examples, or forgetting to create the developer key at /accounts/self/developer_keys before integrating.
Understand the failure class
Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.
Related errors
- invalid_redirect
- @provider.error_message
- the Developer Key is not active or available in this…
- assertion method not supported for this grant_type
- invalid_client_id
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/ddfb0537f39df2d1.
Report an issue: GitHub.
Appendix: source
Thrown at app/controllers/oauth2_provider_controller.rb:52
# browser should be closed automatically. but we'll at least display
# something basic.
return render
end
scopes = (params[:scope] || params[:scopes] || "").split
provider = Canvas::OAuth::Provider.new(
params[:client_id],
params[:redirect_uri],
scopes,
params[:purpose],
pkce: {
code_challenge: params[:code_challenge],
code_challenge_method: params[:code_challenge_method]
}
)
raise Canvas::OAuth::RequestError, :invalid_client_id unless provider.has_valid_key?
raise Canvas::OAuth::RequestError, :invalid_redirect unless provider.has_valid_redirect?
session[:oauth2] = provider.session_hash
session[:oauth2][:state] = params[:state] if params.key?(:state)
session[:oauth2][:nonce] = params[:nonce] if params.key?(:nonce)
if provider.key.require_scopes? && !provider.valid_scopes?
return redirect_to Canvas::OAuth::Provider.final_redirect(self,
state: params[:state],
error: "invalid_scope",
error_description: "A requested scope is invalid, unknown, malformed, or exceeds the scope granted by the resource owner. " \
"The following scopes were requested, but not granted: #{provider.missing_scopes.to_sentence(locale: :en)}")
end
unless provider.key.authorized_for_account?(@domain_root_account)
return redirect_to Canvas::OAuth::Provider.final_redirect(self,
state: params[:state],
error: "unauthorized_client",View on GitHub (pinned to 1c9f0bb801)