instructure/canvas-lms · error
LLM generation is only available for rubrics associated…
Error message
LLM generation is only available for rubrics associated with an Assignment
What it means
validate_rubric_and_association_object requires the association_object passed to LLM criteria generation/regeneration to be an AbstractAssignment. LLM rubric generation depends on assignment context (description, points, grading type), so rubrics associated with non-assignment objects (quizzes, graded surveys, ungraded items) are rejected.
Solutions
- Ensure LLM generation is only invoked from the Assignment rubric editor (assignment must be an AbstractAssignment subclass instance).
- Check the association_object class in the controller and return a clear client error before reaching the service.
- If the feature must support other association types, extend the check or wrap the object in an assignment adapter — but do not bypass the guard.
Example fix
// before
RubricLlmService.new(rubric).generate_criteria_via_llm(association_object: quiz, ...)
// after
unless association_object.is_a?(AbstractAssignment)
return render json: { error: 'LLM rubric generation is only available for assignments' }, status: :unprocessable_entity
end
RubricLlmService.new(rubric).generate_criteria_via_llm(association_object: association_object, ...) Defensive patterns
Strategy: type-guard
Validate before calling
unless association_object.is_a?(AbstractAssignment)
return render json: { error: 'AI rubric generation requires an assignment' }, status: :unprocessable_entity
end Type guard
def assignment_context?(obj) obj.is_a?(AbstractAssignment) end
Try / catch
begin
service.generate_criteria_via_llm(...)
rescue RuntimeError => e
raise unless e.message.include?('only available for rubrics associated with an Assignment')
render json: { error: 'unsupported association type' }, status: :unprocessable_entity
end Prevention
- Only mount the LLM generation flow inside the assignment rubric editor.
- Type-check association_object before constructing RubricLlmService.
- Don't wire the generate button on quiz/account rubric pages.
When it happens
Trigger: Calling generate_criteria_via_llm or regenerate_criteria_via_llm with an association_object that is a Quizzes::Quiz, a DiscussionTopic assignment-less association, a nil object, or any class not inheriting from AbstractAssignment.
Common situations: Rubric created for a quiz or a non-assignment context but the LLM generate button is wired to pass whatever association the page has; controllers pass raw association records of the wrong class; test code passes plain structs.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Cannot find criterion with id #
- Cannot regenerate criteria with learning outcomes attached
- The AI response was not in the expected format. Please try…
- User must be associated to rubric before LLM generation
- AI response appears truncated - the response may have…
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/a8b467e428400b6f.
Report an issue: GitHub.
Appendix: source
Thrown at app/services/rubric_llm_service.rb:191
private
def resolve_generate_options(generate_options)
DEFAULT_GENERATE_OPTIONS.merge(generate_options.symbolize_keys)
end
def resolve_regenerate_options(generate_options, regenerate_options)
resolved = resolve_generate_options(generate_options)
resolved.merge(
additional_user_prompt: regenerate_options.symbolize_keys[:additional_user_prompt].presence ||
resolved[:additional_prompt_info].presence ||
"No specific expectations, just improve it."
)
end
def validate_rubric_and_association_object(association_object)
unless association_object.is_a?(AbstractAssignment)
raise "LLM generation is only available for rubrics associated with an Assignment"
end
raise "User must be associated to rubric before LLM generation" unless @rubric.user
end
def call_llm_with_prefill(llm_config, prompt, root_account_uuid)
response = nil
time = Benchmark.measure do
response = CedarClient.conversation(
messages:
[{ role: "User", text: prompt },
{ role: "Assistant", text: "{" }],
model: llm_config.model_id,
feature_slug: GENERATE_FEATURE_SLUG,
root_account_uuid:,
current_user: @rubric.user
).response
end
[response, time]View on GitHub (pinned to 1c9f0bb801)