instructure/canvas-lms · error · GraphQL::ExecutionError
not found
Error message
not found
What it means
CreateModule#resolve rescues ActiveRecord::RecordNotFound and converts it to GraphQL::ExecutionError 'not found'. The mutation looks up its context (e.g. course from course_id) with find(), so an unknown or inaccessible context ID raises RecordNotFound before any module is created.
Solutions
- Verify the courseId exists and is visible to the current user via a GraphQL course query first.
- Use a Node/context lookup to confirm the ID resolves on the current shard.
- If authorization is the real issue, call as a user enrolled in the course (errors are masked as not-found by design).
- Handle 'not found' errors in the client and prompt the user to re-select the course.
Example fix
// before
const res = await gql(createModuleMutation, { input: { courseId: hardcodedId } })
// after
const course = await gql(getCourseQuery, { id: hardcodedId })
if (!course?.id) throw new Error('Course not accessible')
const res = await gql(createModuleMutation, { input: { courseId: hardcodedId } }) Defensive patterns
Strategy: validation
Validate before calling
const course = await gql(GET_COURSE, { id: courseId })
if (!course) throw new Error(`course ${courseId} not found or not visible`) Type guard
function isResolvableCourse(c) { return c != null && typeof c._id === 'string' } Try / catch
try {
await gql(CREATE_MODULE, { input })
} catch (e) {
if (e.message === 'not found') await promptCourseReselection()
else throw e
} Prevention
- Fetch course ids from GraphQL queries, not hardcoded config.
- Remember Canvas masks authorization failures as not-found.
- Purge ids of deleted courses from stored drafts.
When it happens
Trigger: Calling createModule with a courseId that doesn't exist, belongs to another root account/shard, or that the loader refuses to resolve for an unauthorized user (Canvas loaders raise RecordNotFound for hidden records).
Common situations: Hardcoded course IDs copied between environments; IDs from a deleted course; querying from a token whose user cannot see the course (loaders return not-found instead of forbidden).
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/475c0a19920b219a.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/mutations/create_module.rb:44
field :module, Types::ModuleType, null: true, resolver_method: :will_not_be_called
def will_not_be_called
# This is a silly workaround for https://github.com/rmosolgo/graphql-ruby/issues/2723
end
def resolve(input:)
course_id = GraphQLHelpers.parse_relay_or_legacy_id(input[:course_id], "Course")
course = Course.find(course_id)
verify_authorized_action!(course.context_modules.temp_record, :create)
mod = course.context_modules.build(name: input[:name])
mod.require_presence_of_name = true
if mod.save
{ module: mod }
else
errors_for(mod)
end
rescue ActiveRecord::RecordNotFound
raise GraphQL::ExecutionError, "not found"
end
end
View on GitHub (pinned to 1c9f0bb801)