instructure/canvas-lms · warning · GraphQL::ExecutionError
unable to find LearningOutcome for id
Error message
unable to find LearningOutcome for id %{id} What it means
UpdateLearningOutcome.validate! raises this when no LearningOutcome exists for the supplied id. The mutation resolves the outcome (find_by) and, since it is nil, raises a GraphQL::ExecutionError via validate! during resolve.
Solutions
- Confirm the LearningOutcome id exists (LearningOutcome.find_by(id:) in console).
- Re-fetch the outcome id from the API before mutating; the cached id may be stale.
- Check the outcome was not deleted and you are on the right shard/context.
- Handle the GraphQL error client-side and surface a 'not found' message to the user.
Example fix
// before
updateLearningOutcome(input: {id: "999"}) { ... } // outcome deleted
// after
# const outcome = await fetchOutcome(id); if (!outcome) showNotFound();
updateLearningOutcome(input: {id: "17"}) { ... } Defensive patterns
Strategy: validation
Validate before calling
const outcome = await gql(FETCH_LEARNING_OUTCOME, { id });
if (!outcome?.learningOutcome) throw new Error(`LearningOutcome ${id} not found`); Type guard
function outcomeExists(data) { return data?.learningOutcome != null; } Try / catch
try {
await gql(UPDATE_LEARNING_OUTCOME, { id, ...attrs });
} catch (e) {
if (/unable to find LearningOutcome/.test(e.message)) showNotFound();
else throw e;
} Prevention
- Re-fetch outcome ids before mutating; never use long-cached ids
- Check the outcome wasn't soft/deleted
- Verify environment/shard consistency
When it happens
Trigger: Calling updateLearningOutcome with input.id that does not correspond to any LearningOutcome row (deleted, wrong shard, or never existed).
Common situations: Stale id cached in a client after outcome deletion; cross-account id confusion; data not migrated between environments; typo in the id.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- A course with that id does not exist
- ActiveRecord::RecordNotFound
- Allocation rule not found
- An assignment with that id does not exist
- Assignment not found
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/1c8d0d8576ffacba.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/mutations/update_learning_outcome.rb:50
outcome_input = attrs(input, record.context)
unless account_level_mastery_scales_enabled?(record.context)
update_rubric_criterion(record, outcome_input)
outcome_input.delete(:rubric_criterion)
end
record.saving_user = current_user
if record.update(outcome_input)
{ learning_outcome: record }
else
errors_for(record, { short_description: :title })
end
end
private
def validate!(outcome, outcome_id)
raise GraphQL::ExecutionError, I18n.t("unable to find LearningOutcome for id %{id}", id: outcome_id) unless outcome
raise GraphQL::ExecutionError, I18n.t("insufficient permissions") unless check_permission(outcome)
end
def check_permission(outcome)
outcome.grants_right? current_user, :update
end
def update_rubric_criterion(outcome, input)
return unless input[:rubric_criterion]
mastery_points = input[:rubric_criterion][:mastery_points]
ratings = input[:rubric_criterion][:ratings]
updated_criterion = outcome.rubric_criterion
updated_criterion ||= {}
if mastery_points
updated_criterion[:mastery_points] = mastery_pointsView on GitHub (pinned to 1c9f0bb801)