{"record":{"id":"3308759d49cb8fae","repo":"instructure/canvas-lms","slug":"not-found-base-mutation","errorCode":null,"errorMessage":"not found","messagePattern":"not found","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/base_mutation.rb","lineNumber":55,"sourceCode":"  field :errors, [Types::ValidationErrorType], null: true\n\n  def current_user\n    context[:current_user]\n  end\n\n  def resolve_with_support(**input)\n    # our resolvers generally expect a hash, not GraphQL objects, so just transform it here\n    input_hash = input.deep_transform_values { |v| v.is_a?(GraphQL::Schema::InputObject) ? v.to_h : v }\n\n    super(input: input_hash)\n  end\n\n  def session\n    context[:session]\n  end\n\n  def verify_authorized_action!(obj, perm)\n    raise GraphQL::ExecutionError, \"not found\" unless obj.grants_right?(current_user, session, perm)\n  end\n\n  def verify_any_authorized_actions!(obj, perms)\n    raise GraphQL::ExecutionError, \"not found\" unless obj.grants_any_right?(current_user, session, *Array(perms))\n  end\n\n  # TODO: replace this with model validation where applicable\n  def validation_error(message, attribute: \"message\")\n    {\n      errors: {\n        attribute.to_sym => message\n      }\n    }\n  end\n\n  private\n\n  # returns validation errors in a consistent format (`Types::ValidationError`)","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/base_mutation.rb#L37-L73","documentation":"BaseMutation#verify_authorized_action! raises a GraphQL::ExecutionError 'not found' when obj.grants_right?(current_user, session, perm) is false. To avoid leaking existence information, any authorization failure is reported as if the resource does not exist. It is the standard authorization guard for all Canvas GraphQL mutations.","triggerScenarios":"Any mutation calling verify_authorized_action!(obj, :perm) where the current user lacks the permission: anonymous/unauthenticated requests, wrong enrollment type, user not in the course, or obj not visible to the user.","commonSituations":"A student token attempting a teacher-only mutation; expired session so current_user is nil; querying a resource in a course the user was removed from; API client using a user from a different shard/root account.","solutions":["Confirm the authenticated user actually holds the required permission on the object (check enrollments/roles)","Verify the GraphQL context carries a valid current_user and session (token not expired)","Check the object id resolves to the intended resource in the same account/shard","If the user should have access, fix the role/enrollment; otherwise change the client to use an authorized user"],"exampleFix":"// before\nmutation {$input: ...} // sent as student token\n// after\n// use a token for a user with the required role, or pre-check:\nraise GraphQL::ExecutionError, \"insufficient permissions\" unless course.grants_right?(user, session, :manage_grades)","handlingStrategy":"validation","validationCode":"// client-side pre-check of the user's enrollments/permissions\nconst perms = await canvas.get(`/api/v1/courses/${courseId}/users/self/permissions`)\nif (!perms[requiredPermission]) throw new Error(`user lacks ${requiredPermission}`)","typeGuard":"function isAuthorizedContext(ctx) { return ctx != null && ctx.currentUser != null && ctx.currentUser.id != null }","tryCatchPattern":"try {\n  return await client.mutate({ mutation: MUTATION, variables })\n} catch (e) {\n  if (e.message === 'not found') {\n    // treat as authorization failure: check user role/session, do not retry blindly\n  }\n  throw e\n}","preventionTips":["Remember 'not found' from Canvas mutations usually means permission denied, not missing data","Verify the token/user session is valid and not expired before calling","Check enrollments and role for the target course/account","Avoid masquerading setups that drop current_user from context"],"tags":["graphql","authorization","permissions","mutation"],"backgroundTag":"permission-denied","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}