instructure/canvas-lms · error · UnconfiguredError

an object with an interface for loading settings must be…

Error message

an object with an interface for loading settings must be specified as 'settings_store'

What it means

CanvasSecurity decouples itself from Canvas's Setting class: consumers must inject an object exposing the settings-loading interface via CanvasSecurity.settings_store = .... Reading CanvasSecurity.settings_store when none was injected raises UnconfiguredError (unless safe_invoke: true, which returns nil).

Solutions

  1. Add `CanvasSecurity.settings_store = Setting` (or an equivalent object responding to the settings interface) in an initializer
  2. Ensure the initializer runs before any code path touching CanvasSecurity (fix boot/autoload ordering)
  3. Use `settings_store(safe_invoke: true)` in code that tolerates an unconfigured store

Example fix

// before
CanvasSecurity::ServicesJwt.for_user(domain, user)
// after
# config/initializers/canvas_security.rb
CanvasSecurity.settings_store = Setting
CanvasSecurity::ServicesJwt.for_user(domain, user)
Defensive patterns

Strategy: validation

Validate before calling

CanvasSecurity.settings_store(safe_invoke: true) or raise 'CanvasSecurity not configured'

Type guard

def canvas_security_configured? = !CanvasSecurity.settings_store(safe_invoke: true).nil?

Try / catch

begin
  CanvasSecurity::ServicesJwt.for_user(domain, user)
rescue CanvasSecurity::UnconfiguredError
  configure_canvas_security!
  retry
end

Prevention

When it happens

Trigger: Invoking any CanvasSecurity API that reads settings (e.g. jwt encryption key lookups) before the host app assigned CanvasSecurity.settings_store = SomeStore.

Common situations: Using the canvas_security gem standalone or in a new service without initializer configuration; boot-order issues where CanvasSecurity is used before initializers run; test suites without the usual Canvas initializer.

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — 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/44d9f01c3e873e2a. Report an issue: GitHub.

Appendix: source

Thrown at gems/canvas_security/lib/canvas_security.rb:53

  # TODO: Maybe at one point Setting will be
  # a gem on it's own or some other dependable module.
  # For the moment, this is a convenient way to inject
  # this base class without needing to depend on it directly.
  mattr_writer :settings_store
  mattr_accessor :region, :environment

  # Expected interface for this object is:
  #   object.get(setting_name, 'default_value') # [ returning a string ]
  #
  # In this instance, it's expected that canvas is going to inject
  # the Setting class, but we want to break depednencies that directly
  # point to canvas.
  def self.settings_store(safe_invoke: false)
    return @@settings_store if @@settings_store
    return nil if safe_invoke

    raise UnconfiguredError, "an object with an interface for loading settings must be specified as 'settings_store'"
  end

  class AuthenticationError < RuntimeError
    def response_status
      401
    end
  end

  class InvalidToken < AuthenticationError
  end

  class TokenExpired < AuthenticationError
  end

  class InvalidJwtKey < AuthenticationError
  end

  def self.encryption_key

View on GitHub (pinned to 1c9f0bb801)