instructure/canvas-lms · error · ToolDisabled
tool_key
Error message
tool_key
What it means
StudyAssist::InvalidPrompt (raised as 'Unsupported prompt') is thrown by StudyAssist#call when the user's prompt does not match any registered tool's :prompt_pattern in the TOOLS hash. The service routes a free-form prompt to a specific AI tool; if no regex matches, it cannot proceed and raises before checking feature flags or calling the LLM.
Solutions
- Check the prompt against the TOOLS prompt_pattern regexes in app/services/study_assist.rb and fix the wording to match
- Register a new prompt_pattern in the TOOLS constant if this is a legitimate new intent
- Have the caller pass an explicit tool selection instead of relying on prompt regex matching
- Normalize the prompt (strip, downcase) before matching if the patterns expect canonical form
Example fix
// before
StudyAssist.new(course: @course, user: @user, prompt: params[:prompt]).call
// after
prompt = params[:prompt].to_s.strip
raise ActionController::BadRequest unless StudyAssist::TOOLS.any? { |_, cfg| prompt.match?(cfg[:prompt_pattern]) }
StudyAssist.new(course: @course, user: @user, prompt: prompt).call Defensive patterns
Strategy: validation
Validate before calling
raise unless prompt.present? && StudyAssist::TOOLS.any? { |_, cfg| prompt.match?(cfg[:prompt_pattern]) } Type guard
null
Try / catch
begin
StudyAssist.new(course:, user:, prompt:).call
rescue StudyAssist::InvalidPrompt => e
render json: { error: e.message }, status: :bad_request
end Prevention
- Keep prompt regexes in TOOLS and UI wording in sync
- Normalize/strip prompt text before calling
- Log unmatched prompts to detect missing patterns
- Prefer explicit tool selection over prompt matching where possible
When it happens
Trigger: Calling StudyAssist.new(course:, user:, prompt: '<text>' ...).call where the prompt text matches none of the TOOLS regex prompt_patterns (e.g. vague or typo'd instructions, prompt for a tool not registered in TOOLS).
Common situations: Frontend sends a user prompt verbatim instead of a selected tool_key; new tool added to UI but prompt_pattern not registered in TOOLS; localized/translated prompts don't match English regex patterns; whitespace/casing differences defeat the regex.
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
- No pageID or fileID provided
- An institutional tag category did not pass validation
- app '# ' must be one of: #
- cannot change column: captions - locked by Master Course
- Cannot validate existence for resource type: #
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/e34ade7f37b5fa4a.
Report an issue: GitHub.
Appendix: source
Thrown at app/services/study_assist.rb:103
end
def initialize(course:, user:, prompt:, state:, locale: I18n.locale.to_s, regenerate: false)
@course = course
@user = user
@prompt = prompt.to_s
@state = state || {}
@locale = locale
@regenerate = regenerate || @prompt.match?(REGENERATE_PROMPT_PATTERN)
end
def call
return build_chips if @prompt.blank?
tool_key, tool_config = TOOLS.find { |_, cfg| @prompt.match?(cfg[:prompt_pattern]) }
raise InvalidPrompt, "Unsupported prompt" unless tool_key
unless @course.feature_enabled?(:study_assist) && @course.feature_enabled?(tool_config[:feature_flag])
raise ToolDisabled, tool_key.to_s
end
content = resolve_content
llm_config = LLMConfigs.config_for(tool_config[:llm_config])
raise "No LLM config found for #{tool_config[:llm_config]}" if llm_config.nil?
cache_key = response_cache_key(tool_key, llm_config, content)
Rails.cache.delete(cache_key) if @regenerate
Rails.cache.fetch(cache_key, expires_in: RESPONSE_CACHE_TTL) do
InstLLMHelper.with_rate_limit(user: @user, llm_config:) do
raw = call_cedar(tool_key, llm_config, content)
build_response(tool_key, raw)
end
end
rescue InstLLMHelper::RateLimitExceededError => e
Rails.logger.warn("Study Assist rate limit exceeded for #{tool_key}: #{e.message}")View on GitHub (pinned to 1c9f0bb801)