instructure/canvas-lms · error · ArgumentError
Template must be a string
Error message
Template must be a string
What it means
LlmConfig#validate! raises ArgumentError when the template passed to the initializer is not a String. The prompt template is required to be a string containing the placeholder tokens that generate_prompt_and_options will substitute.
Solutions
- Pass the fully rendered template String.
- Join prompt parts with .join("\n") before constructing the config.
- Fix the config source so the template field is present and a string.
Example fix
// before
LlmConfig.new("gpt4", "gpt-4", nil, ["Hello", "<NAME_PLACEHOLDER>"], {})
// after
LlmConfig.new("gpt4", "gpt-4", nil, "Hello <NAME_PLACEHOLDER>", {}) Defensive patterns
Strategy: type-guard
Validate before calling
raise ArgumentError, "template must be String" unless template.is_a?(String)
Type guard
def valid_template?(v) = v.is_a?(String)
Try / catch
begin
cfg = LlmConfig.new(name, model_id, rate_limit, template, options)
rescue ArgumentError => e
Rails.logger.error("Invalid template: #{e.message}")
raise
end Prevention
- Join multi-part prompts with .join("\n") before constructing the config
- Fail fast on nil template when loading config files
When it happens
Trigger: LlmConfig.new(name, model_id, rate_limit, template, ...) where template is nil, an array of prompt parts, a proc, or any non-string value.
Common situations: Building templates by joining arrays and forgetting to join; nil template from a missing config field; passing a lazily rendered block instead of a rendered string.
Related errors
- Model ID must be a string
- Name must be a string
- Options must be a hash
- Rate limit must be either nil, or hash with :limit and…
- A course did not pass validation
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/8b5e7dafbc4c42c4.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/llm_config.rb:66
end
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)