instructure/canvas-lms · error · GraphQL::ExecutionError
group not found
Error message
group not found
What it means
In CreateLearningOutcome, the private helper learning_outcome_group looks up LearningOutcomeGroup.active.find_by(id: input[:group_id]) and raises GraphQL::ExecutionError 'group not found' when no active group matches. 'Active' means not soft-deleted (workflow_state not deleted), so soft-deleted groups also produce this error even though the row exists.
Solutions
- Verify the group_id refers to an existing, non-deleted LearningOutcomeGroup (check workflow_state = 'active').
- Re-fetch the group via the GraphQL query before mutating, and use the fresh ID.
- Confirm you are calling on the same shard/root account where the group lives.
- Ensure the client sends the group_id argument (required by this mutation's flow) and it parses as a valid ID.
Example fix
// before
createLearningOutcome(input: { title, groupOfId: staleId })
// after
const group = await fetchGroupById(staleId) // verify active
const id = group?.workflowState === 'active' ? group._id : await createOrPickGroup()
createLearningOutcome(input: { title, groupId: id }) Defensive patterns
Strategy: validation
Validate before calling
const group = await gql(GET_OUTCOME_GROUP, { id: groupId })
if (!group || group.workflowState !== 'active') throw new Error(`outcome group ${groupId} not found/deleted`) Type guard
function isActiveGroup(g) { return g != null && g.workflowState === 'active' && typeof g._id === 'string' } Try / catch
try {
await gql(CREATE_OUTCOME, { input })
} catch (e) {
if (e.message === 'group not found') await reselectGroupAndRetry()
else throw e
} Prevention
- Never cache LearningOutcomeGroup ids across environments.
- Refresh group lists before mutations instead of using stale ids.
- Treat deleted groups (workflow_state 'deleted') as gone in UI state.
When it happens
Trigger: Calling createLearningOutcome with group_id that is: nil/omitted, a malformed ID, an ID from another shard/root account, or the ID of a soft-deleted LearningOutcomeGroup.
Common situations: Client caches a group ID that was later deleted; copying group IDs between test and production Canvas; forgetting that global outcome groups vs context groups have different ID spaces; passing a string instead of an ID type is coerced to nil.
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
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/e52573406bc5e6e3.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/mutations/create_learning_outcome.rb:44
def resolve(input:)
outcome_group = learning_outcome_group(input)
outcome_input = attrs(input, outcome_group)
record = LearningOutcome.new(context: outcome_group.context, **outcome_input)
record.saving_user = current_user
check_permission(record)
return errors_for(record) unless record.save
outcome_group.add_outcome(record)
{ learning_outcome: record }
end
private
def learning_outcome_group(input)
LearningOutcomeGroup.active.find_by(id: input[:group_id]).tap do |group|
raise GraphQL::ExecutionError, I18n.t("group not found") if group.nil?
end
end
def check_permission(outcome)
raise GraphQL::ExecutionError, I18n.t("insufficient permission") unless outcome.grants_right? current_user, :create
end
end
View on GitHub (pinned to 1c9f0bb801)