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

  1. Check the prompt against the TOOLS prompt_pattern regexes in app/services/study_assist.rb and fix the wording to match
  2. Register a new prompt_pattern in the TOOLS constant if this is a legitimate new intent
  3. Have the caller pass an explicit tool selection instead of relying on prompt regex matching
  4. 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

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


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)