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

  1. Wrap the value in a hash: :thumbnails => { :thumb => '100x100' }
  2. If sizes come from config, coerce: Array(cfg).to_h or JSON parse then verify is_a?(Hash)
  3. Use :thumbnail_class / single-thumbnail options if you only need one thumbnail and no hash
  4. 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

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


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)