{"record":{"id":"75e742a19aca1b75","repo":"instructure/canvas-lms","slug":"assignment-not-found-create-comment-bank-item","errorCode":null,"errorMessage":"Assignment not found","messagePattern":"Assignment not found","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/create_comment_bank_item.rb","lineNumber":50,"sourceCode":"    record = CommentBankItem.new(course:, user: current_user, comment: input[:comment], assignment:)\n    verify_authorized_action!(record, :create)\n    return errors_for(record) unless record.save\n\n    { comment_bank_item: record }\n  end\n\n  private\n\n  def get_course(course_id)\n    Course.active.find_by(id: course_id).tap do |course|\n      raise GraphQL::ExecutionError, I18n.t(\"Course not found\") if course.nil?\n    end\n  end\n\n  def get_assignment(assignment_id, course)\n    AbstractAssignment.assignment_scope_for_context(course).active.find(assignment_id)\n  rescue ActiveRecord::RecordNotFound\n    raise GraphQL::ExecutionError, I18n.t(\"Assignment not found\")\n  end\nend\n","sourceCodeStart":32,"sourceCodeEnd":53,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/create_comment_bank_item.rb#L32-L53","documentation":"create_comment_bank_item#get_assignment looks up the assignment via AbstractAssignment.assignment_scope_for_context(course).active.find(assignment_id); ActiveRecord::RecordNotFound is rescued and re-raised as 'Assignment not found'. The scope restricts to assignments visible in that course context, so mismatched course/assignment pairs also fail.","triggerScenarios":"Calling createCommentBankItem with an assignmentId that doesn't belong to the given course, is deleted, is not in the context's assignment scope (e.g. subassignment/quiz type excluded), or a nonexistent id.","commonSituations":"Assignment id taken from a different course than the courseId supplied; assignment soft-deleted; wiki/LTI assignment types filtered out by assignment_scope_for_context; cross-shard id.","solutions":["Confirm the assignment belongs to the same course passed as courseId and is active","Check the id is in the scope: AbstractAssignment.assignment_scope_for_context(course).active.find(id) in console","Use fresh assignment ids from the course's assignments query rather than cached values","Verify shard/id format (legacy numeric vs relay id)"],"exampleFix":"// before\ncreateCommentBankItem(input: { courseId: \"1\", assignmentId: \"999\" }) // assignment in course 2\n// after\nassignment = course.assignments.active.first\ncreateCommentBankItem(input: { courseId: \"1\", assignmentId: assignment.id.to_s })","handlingStrategy":"validation","validationCode":"const assignments = await canvas.get(`/api/v1/courses/${courseId}/assignments`)\nif (!assignments.some(a => String(a.id) === String(assignmentId))) throw new Error('assignment not in this course or inactive')","typeGuard":"function belongsToCourse(a, courseId) { return a != null && a.course_id != null && String(a.course_id) === String(courseId) && !a.deleted_at }","tryCatchPattern":"try {\n  await client.mutate({ mutation: CREATE_COMMENT_BANK_ITEM, variables: { courseId, assignmentId } })\n} catch (e) {\n  if (e.message.includes('Assignment not found')) {\n    // verify course/assignment pairing, then refetch ids\n  }\n  throw e\n}","preventionTips":["Only pair assignment ids with the course they belong to","Refetch assignments rather than caching ids across terms","Exclude assignment types filtered by the context scope (quizzes/LTI) where relevant","Normalize both ids to the same format before calling"],"tags":["graphql","mutation","assignment","record-not-found"],"backgroundTag":"record-not-found","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"}