instructure/canvas-lms · error · ArgumentError

Options must be a hash

Error message

Options must be a hash

What it means

LlmConfig#validate! raises ArgumentError when options is not a Hash. options holds per-request LLM parameters (temperature, messages, etc.) and must be a hash that generate_prompt_and_options can deep_dup and substitute into.

Solutions

  1. Pass a Hash (use {} if no options are needed).
  2. Parse JSON option blobs with JSON.parse before constructing the config.
  3. Fix the YAML/JSON config so options is a mapping, not a list.

Example fix

// before
LlmConfig.new("gpt4", "gpt-4", nil, template, nil)
// after
LlmConfig.new("gpt4", "gpt-4", nil, template, {temperature: 0.7})
Defensive patterns

Strategy: type-guard

Validate before calling

raise ArgumentError, "options must be Hash" unless options.is_a?(Hash)

Type guard

def valid_options?(v) = v.is_a?(Hash)

Try / catch

begin
  cfg = LlmConfig.new(name, model_id, rate_limit, template, options)
rescue ArgumentError => e
  Rails.logger.error("Invalid options: #{e.message}")
  raise
end

Prevention

When it happens

Trigger: LlmConfig.new(name, model_id, rate_limit, template, options) where options is nil, an array, or another non-hash value.

Common situations: Omitting the options argument entirely (nil); YAML config where options is a list; passing JSON string instead of a parsed hash.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/94eca7e0f4e5bb8a. Report an issue: GitHub.

Appendix: source

Thrown at app/models/llm_config.rb:67

    end

    new_options.each_value do |value|
      if value.is_a?(String) && (remaining_placeholder = value.match(/<\w+_PLACEHOLDER>/))
        raise ArgumentError, "Options still contain placeholder: #{remaining_placeholder[0]}"
      end
    end

    [new_template, new_options]
  end

  private

  def validate!
    raise ArgumentError, "Name must be a string" unless @name.is_a?(String)
    raise ArgumentError, "Model ID must be a string" unless @model_id.is_a?(String)
    raise ArgumentError, "Rate limit must be either nil, or hash with :limit and :period keys" unless @rate_limit.nil? || (@rate_limit.is_a?(Hash) && @rate_limit.keys == %i[limit period])
    raise ArgumentError, "Template must be a string" unless @template.is_a?(String)
    raise ArgumentError, "Options must be a hash" unless @options.is_a?(Hash)
  end
end

View on GitHub (pinned to 1c9f0bb801)