instructure/canvas-lms · error · ArgumentError
:thumbnails option should be a hash: e.g. :thumbnails =>
Error message
:thumbnails option should be a hash: e.g. :thumbnails => { :foo => '50x50' } What it means
attachment_fu's has_attachment validates its options: the :thumbnails option must be a Hash mapping thumbnail names to size strings (e.g. { :thumb => '50x50' }). Passing a non-Hash (string, array, nil handled separately by ||={}) raises ArgumentError at class-definition time, so the model fails to load.
Solutions
- Wrap the value in a hash: :thumbnails => { :thumb => '100x100' }
- If sizes come from config, coerce: Array(cfg).to_h or JSON parse then verify is_a?(Hash)
- Use :thumbnail_class / single-thumbnail options if you only need one thumbnail and no hash
- Freeze/validate the constant feeding :thumbnails in an initializer test so model load fails fast in CI
Example fix
// before
has_attachment :thumbnails => '50x50'
// after
has_attachment :thumbnails => { :thumb => '50x50' } Defensive patterns
Strategy: type-guard
Validate before calling
thumbs = opts[:thumbnails] raise ':thumbnails must be a Hash' unless thumbs.nil? || thumbs.is_a?(Hash) has_attachment :thumbnails => thumbs
Type guard
def valid_thumbnails?(opts) opts[:thumbnails].nil? || opts[:thumbnails].is_a?(Hash) end
Try / catch
begin
ModelWithBadAttachment # model loads has_attachment at class time
rescue ArgumentError => e
raise if e.message.exclude?(':thumbnails option')
Rails.logger.error('fix :thumbnails to a Hash of name => size')
end Prevention
- Always pass :thumbnails as { name => 'WxH' } hash literal
- Validate config-sourced thumbnail settings parse to a Hash before loading models
- Add a smoke test that eager-loads attachment models so bad options fail in CI
When it happens
Trigger: Declaring `has_attachment :thumbnails => '100x100'` (a String), `:thumbnails => [:thumb]` (an Array), or building the value dynamically into something that isn't a Hash in a Thumbnail/attachment model.
Common situations: Copy-paste from docs where the braces were lost; config stored in YAML/DB returning a string; refactoring that swapped the hash for a list of names; typos like :thumbnail (singular) leaking into :thumbnails.
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
- unknown keyword(s): #
- 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/82d5fd17ecf4cc76.
Report an issue: GitHub.
Appendix: source
Thrown at gems/attachment_fu/lib/attachment_fu.rb:107
# has_attachment :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }
# has_attachment :storage => :file_system, :path_prefix => 'public/files'
# has_attachment :storage => :file_system, :path_prefix => 'public/files',
# :content_type => :image, :resize_to => [50,50]
# has_attachment :storage => :file_system, :path_prefix => 'public/files',
# :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }
# has_attachment :storage => :s3
def has_attachment(options = {})
# this allows you to redefine the acts' options for each subclass, however
options[:min_size] ||= 1
options[:max_size] ||= 1.megabyte
options[:size] ||= (options[:min_size]..options[:max_size])
options[:thumbnails] ||= {}
options[:thumbnail_class] ||= self
options[:s3_access] ||= "public-read"
options[:content_type] = [options[:content_type]].flatten.collect! { |t| (t == :image) ? AttachmentFu.content_types : t }.flatten unless options[:content_type].nil?
unless options[:thumbnails].is_a?(Hash)
raise ArgumentError, ":thumbnails option should be a hash: e.g. :thumbnails => { :foo => '50x50' }"
end
extend ClassMethods unless (class << self; included_modules; end).include?(ClassMethods)
include InstanceMethods unless include?(InstanceMethods)
parent_options = attachment_options || {}
# doing these shenanigans so that #attachment_options is available to processors and backends
self.attachment_options = options
attr_accessor :thumbnail_resize_options
attachment_options[:storage] ||= (attachment_options[:file_system_path] || attachment_options[:path_prefix]) ? :file_system : :db_file
attachment_options[:storage] ||= parent_options[:storage]
attachment_options[:path_prefix] ||= attachment_options[:file_system_path]
if attachment_options[:path_prefix].nil?
attachment_options[:path_prefix] = (attachment_options[:storage] == :s3) ? table_name : File.join("public", table_name)
end
attachment_options[:path_prefix] = attachment_options[:path_prefix][1..] if options[:path_prefix].first == "/"View on GitHub (pinned to 1c9f0bb801)