instructure/canvas-lms · error · ArgumentError
unknown keyword(s): #
Error message
unknown keyword(s): #{unknown_kwargs.map(&:inspect).join(", ")} What it means
PrefixProxy#fetch accepts only `ttl`, `failsafe_cache`, and a whitelisted `failsafe` keyword. Any other keyword argument is rejected with ArgumentError before any cache or Consul lookup happens. This guards against callers passing stale or typosquatted options (e.g. from an older fetch API) down to fetch_without_request_cache.
Solutions
- Remove the unsupported keyword(s) from the fetch call; the message names them exactly
- To supply a fallback, fetch and rescue nil-or-handle yourself instead of passing `default:`
- Use `failsafe: true` (the only extra allowed kwarg) if you need the on-disk failsafe cache path
- Check the canvas dynamic_settings docs/changelog for renamed options
Example fix
// before
settings.fetch('canvas/statsd/namespace', default: 'canvas')
// after
settings.fetch('canvas/statsd/namespace') || 'canvas' Defensive patterns
Strategy: validation
Validate before calling
def fetch_settings(proxy, key, **opts)
allowed = %i[ttl failsafe_cache failsafe]
bad = opts.keys - allowed
raise ArgumentError, "unsupported fetch opts: #{bad}" if bad.any?
proxy.fetch(key, **opts)
end Try / catch
begin
val = proxy.fetch(key)
rescue ArgumentError => e
Rails.logger.warn("DynamicSettings fetch kwarg rejected: #{e.message}")
val = nil
end Prevention
- Wrap DynamicSettings access in a thin helper exposing only supported options
- Grep for legacy fetch kwargs when upgrading Canvas
- Use keyword-args-aware linters to catch unknown options
When it happens
Trigger: Calling DynamicSettings.find(...).fetch('some/key', timeout: 5) or any fetch with an unsupported keyword such as `cache:` or `default:`; also calling through a wrapper that forwards kwargs indiscriminately.
Common situations: Upgrading Canvas: older DynamicSettings fetch options were removed; copy-pasted code from other config loaders passing Ruby Hash#fetch-style options like `default:`; typo in a keyword name.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- :thumbnails option should be a hash: e.g. :thumbnails =>
- an object with an interface for loading settings must be…
- an object with an interface for loading settings must be…
- block is required if module_name is not passed
- Cannot include a concatenation in a merge.
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/24fb675d4695e709.
Report an issue: GitHub.
Appendix: source
Thrown at gems/dynamic_settings/lib/dynamic_settings/prefix_proxy.rb:85
# Fetch the value at the requested key using the prefix passed to the
# initializer.
#
# This method is intended to retreive a single key from the keyspace and
# will not work for getting multiple values in a hash from the store. If
# you need to access values nested deeper in the keyspace use #for_prefix
# to move deeper in the nesting.
#
# @param key [String, Symbol] The key to fetch
# @param ttl [ActiveSupport::Duration] The TTL for the value in the cache,
# defaults to value supplied to the constructor.
# @param failsafe_cache [false, PathInfo] Location on disk to store a
# failsafe cached for this value, in case Consul is down on a future boot.
# Should be used sparingly, since it will load a file off disk.
# @return [String]
# @return [nil] When no value was found
def fetch(key, ttl: @default_ttl, failsafe_cache: false, **kwargs)
unknown_kwargs = kwargs.keys - [:failsafe]
raise ArgumentError, "unknown keyword(s): #{unknown_kwargs.map(&:inspect).join(", ")}" unless unknown_kwargs.empty?
# Within a given request, no reason to talk to redis/consul multiple times for the same key in the same tree
# The TTL is only relevant for the underlying cache-within a request we don't exceed the ttl boundary
DynamicSettings.request_cache.cache(CACHE_KEY_PREFIX + full_key(key)) do
fetch_without_request_cache(key, ttl:, failsafe_cache:, **kwargs)
end
end
alias_method :[], :fetch
# Extend the prefix from this instance returning a new one.
#
# @param prefix_extension [String]
# @param default_ttl [ActiveSupport::Duration] The default TTL to use when
# fetching keys from the extended keyspace, defaults to the same value as
# the receiver
# @return [ProxyPrefix]
def for_prefix(prefix_extension, default_ttl: @default_ttl)
self.class.new(View on GitHub (pinned to 1c9f0bb801)