instructure/canvas-lms · error · Canvas::OAuth::RequestError
invalid_client_id
invalid_client_id
Error message
invalid_client_id
What it means
Canvas::OAuth::RequestError :invalid_client_id is raised in Canvas::OAuth::GrantTypes::BaseType#validate_client_id_and_secret when @provider.has_valid_key? returns false, meaning the client_id supplied to the token endpoint does not resolve to an existing, active (non-deleted, workflow-active) Canvas developer key. The grant type cannot proceed without a valid client identification.
Solutions
- Verify the client_id against an active developer key in Canvas account > Developer Keys.
- Re-create or re-activate the developer key if it was deleted or turned off, and update the app's configuration.
- Confirm the token request targets the correct Canvas host/account where the key exists.
- Check that the client_id parameter is actually sent (not dropped by config/env loading) and matches the key's global id format.
Example fix
// before: hardcoded id from another environment
const CLIENT_ID = '10000000000123'
// after: load from environment config verified in the target account
const CLIENT_ID = process.env.CANVAS_CLIENT_ID
if (!CLIENT_ID) throw new Error('CANVAS_CLIENT_ID not configured') Defensive patterns
Strategy: validation
Validate before calling
function validateClientId(clientId) { return typeof clientId === 'string' && /^\d+$/.test(clientId.trim()) } // non-empty numeric Canvas developer key id Type guard
function hasClientId(cfg) { return typeof cfg.clientId === 'string' && cfg.clientId.trim() !== '' } Try / catch
try {
token = await exchangeCode(code, clientId, clientSecret)
} catch (e) {
if (e.body?.error === 'invalid_client_id') {
throw new Error(`Developer key ${clientId} not found/active in Canvas — check account Developer Keys`)
}
throw e
} Prevention
- Keep client_id in environment config, never hardcoded, and verify it after each deploy against the target Canvas account.
- Alert on developer key deactivation/deletion in Canvas admin.
- Sanity-check client_id (numeric, present) before every token request.
When it happens
Trigger: POST to /login/oauth2/token with a client_id that has no matching DeveloperKey row in the Canvas account, references a deleted/inactive key, uses the local id where the global id (or vice versa) is required across shards, or omits client_id entirely.
Common situations: Typo in client_id or copying the key's id instead of its client_id; developer key deleted or set to inactive by an admin; pointing the app at a different Canvas account/environment than where the key was created; shard mismatch where the key exists on another shard.
Related errors
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/5fa64ff54fcbd6de.
Report an issue: GitHub.
Appendix: source
Thrown at lib/canvas/oauth/grant_types/base_type.rb:33
validate_client_id_and_secret
validate_type
generate_token
end
# Unless otherwise specified by a sub-class, don't
# allow public clients as defined in RFC 6749.
def allow_public_client?
false
end
def supported_type?
false
end
private
def validate_client_id_and_secret
raise Canvas::OAuth::RequestError, :invalid_client_id unless @provider.has_valid_key?
# Issue an access token if the grant type supports public client and the
# DeveloperKey identifies a public client. Otherwise, the client must must
# provide a client secret.
return if allow_public_client? && @provider.key&.public_client? && @secret.blank?
raise Canvas::OAuth::RequestError, :invalid_client_secret unless @provider.is_authorized_by?(@secret)
end
def validate_type
raise "Abstract Method"
end
def generate_token
raise "Abstract Method"
end
end
end
endView on GitHub (pinned to 1c9f0bb801)