instructure/canvas-lms · critical
Please never delete the default developer key
Error message
Please never delete the default developer key
What it means
DeveloperKey#protect_default_key is a before_destroy-style validation guard: the site-wide default developer key (used for user-generated access tokens) must always remain in the 'active' workflow state and can never be deleted or deactivated. Raising prevents silent destruction of this critical singleton.
Solutions
- Remove the default key from any destroy/deactivate list: skip records where dk == DeveloperKey.default
- Only operate on non-default keys: scope deletions with where.not(id: DeveloperKey.default.id)
- If you truly need a new default key, follow Canvas's documented default-key regeneration path, never just delete it
- In specs, avoid clearing all DeveloperKeys; exclude the seeded default
Example fix
// before
DeveloperKey.where("updated_at < ?", 1.year.ago).destroy_all
// after
DeveloperKey.where("updated_at < ?", 1.year.ago).
where.not(id: DeveloperKey.default.id).destroy_all Defensive patterns
Strategy: validation
Validate before calling
raise 'refusing to delete default key' if key == DeveloperKey.default key.destroy
Try / catch
begin
key.destroy!
rescue RuntimeError => e
Rails.logger.warn("skipped default key: #{e.message}")
end Prevention
- Exclude DeveloperKey.default from any bulk delete/deactivate query
- Audit cleanup scripts for unscoped DeveloperKey destruction
- Treat the default key as immutable infrastructure
When it happens
Trigger: Destroying or setting workflow_state != 'active' on the DeveloperKey for which self == DeveloperKey.default (the special default key seeded per shard/site).
Common situations: Cleanup scripts deleting stale/unused developer keys that accidentally include the default key; test factories or seeds wiping keys; console `dk.destroy!` on the default key while deactivating unused integrations.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Please don't turn off the default developer key
- assessor and assessee required
- Assignment not configured for external tool launches
- association required
- can only forward one conversation at a time
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/4bbee299f3f8c207.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/developer_key.rb:199
def ims_registration?
ims_registration.present?
end
def validate_redirect_uris
uris = redirect_uris&.map do |value|
value, _ = CanvasHttp.validate_url(value, allowed_schemes: nil)
value
end
errors.add :redirect_uris, "a redirect_uri is too long" if uris.any? { |uri| uri.length > 4096 }
self.redirect_uris = uris unless uris == redirect_uris
rescue CanvasHttp::Error, URI::Error, ArgumentError
errors.add :redirect_uris, "is not a valid URI"
end
def protect_default_key
raise "Please never delete the default developer key" if workflow_state != "active" && self == self.class.default
end
def nullify_empty_icon_url
self.icon_url = nil if icon_url.blank?
end
def generate_api_key(overwrite: false)
self.api_key = CanvasSlug.generate(nil, 64) if overwrite || !api_key
end
def generate_rsa_keypair!(overwrite: false)
return if public_jwk.present? && !overwrite
key_pair = CanvasSecurity::RSAKeyPair.new
@private_jwk = key_pair.to_jwk
self.public_jwk = key_pair.public_jwk.to_h
end
View on GitHub (pinned to 1c9f0bb801)