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

  1. Remove the default key from any destroy/deactivate list: skip records where dk == DeveloperKey.default
  2. Only operate on non-default keys: scope deletions with where.not(id: DeveloperKey.default.id)
  3. If you truly need a new default key, follow Canvas's documented default-key regeneration path, never just delete it
  4. 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

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


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)