instructure/canvas-lms · warning · InvalidPrompt
Unsupported prompt
Error message
Unsupported prompt
What it means
StudyAssist::InvalidPrompt raised by StudyAssist#call when the user's prompt matches none of the TOOLS prompt_pattern regexes. StudyAssist routes free-form prompts to a specific tool (flashcards, quizzes, etc.); unrouteable prompts are rejected before any tool runs.
Solutions
- Use one of the supported prompt forms matched by the TOOLS prompt_patterns (e.g. 'make flashcards about ...')
- Inspect StudyAssist::TOOLS to see the accepted prompt patterns
- Update the prompt_pattern regexes to cover new phrasings if the tool should handle them
- Handle InvalidPrompt in the controller/API layer and show the user the supported prompts
Example fix
// before
StudyAssist.new(course:, prompt: params[:prompt]).call
// after
begin
StudyAssist.new(course:, prompt: params[:prompt]).call
rescue StudyAssist::InvalidPrompt
render json: { error: 'Unsupported prompt', supported: StudyAssist::TOOLS.keys }, status: :bad_request
end Defensive patterns
Strategy: try-catch
Validate before calling
supported = StudyAssist::TOOLS.any? { |_, cfg| prompt.match?(cfg[:prompt_pattern]) }
raise ArgumentError, 'unsupported prompt' unless supported Type guard
null
Try / catch
begin StudyAssist.new(course:, prompt:).call rescue StudyAssist::InvalidPrompt # return 400 with the list of supported prompts rescue StudyAssist::ToolDisabled => e # return 403 indicating the feature flag is off for this course end
Prevention
- Constrain frontend input to predefined chip prompts
- Keep TOOLS prompt_patterns in sync with UI copy
- Enable study_assist and the tool's feature_flag on the course before calling
- Handle both InvalidPrompt and ToolDisabled at the API boundary
When it happens
Trigger: Calling StudyAssist.new(course:, prompt: "some free text").call where the prompt does not match any tool's prompt_pattern regex, and is not blank (blank prompts build chips instead).
Common situations: Users typing generic questions ('help me study') that no regex matches; localized/non-English prompts; TOOLS patterns narrowed by a recent change; frontend sending raw input instead of a predefined chip prompt.
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
- The AI response was not in the expected format. Please try…
- Content exceeds # character limit
- Rate limit exceeded
- assessor and assessee required
- association required
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/5d75dcdfcc914705.
Report an issue: GitHub.
Appendix: source
Thrown at app/services/study_assist.rb:100
return :chips if prompt.blank?
TOOLS.find { |_, cfg| prompt.match?(cfg[:prompt_pattern]) }&.first || :unknown
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)
endView on GitHub (pinned to 1c9f0bb801)