{"record":{"id":"0f1e57a135cc0da8","repo":"instructure/canvas-lms","slug":"course-not-found-create-comment-bank-item","errorCode":null,"errorMessage":"Course not found","messagePattern":"Course not found","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/create_comment_bank_item.rb","lineNumber":43,"sourceCode":"  argument :comment, String, required: true\n  argument :course_id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func(\"Course\")\n  field :comment_bank_item, Types::CommentBankItemType, null: true\n\n  def resolve(input:)\n    course = get_course(input[:course_id])\n    assignment = get_assignment(input[:assignment_id], course) if input[:assignment_id]\n    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":25,"sourceCodeEnd":53,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/create_comment_bank_item.rb#L25-L53","documentation":"create_comment_bank_item#get_course uses Course.active.find_by(id: course_id) and raises 'Course not found' when nil. Because it uses the active scope, deleted or concluded courses are treated as missing even if the row exists.","triggerScenarios":"Calling createCommentBankItem with a courseId that is nil, deleted/concluded, nonexistent, or from a different shard/root account context.","commonSituations":"Client passes context code-derived id from a course since deleted; id format mismatch (relay id vs legacy id); course on another shard not resolved in the GraphQL context.","solutions":["Verify Course.active.find_by(id: course_id) returns a record in rails console","Pass a valid legacy numeric course id in the correct shard","Re-fetch course ids from the API if the course may have been deleted","Ensure the user's GraphQL context resolves the course's root account"],"exampleFix":"// before\nget_course(nil) // course_id not resolved from context_code\n// after\ncourse = Course.active.find_by(id: parsed_course_id)\nraise GraphQL::ExecutionError, \"Course not found\" unless course","handlingStrategy":"validation","validationCode":"let course\ntry { course = await canvas.get(`/api/v1/courses/${courseId}`) } catch { course = null }\nif (!course || course.workflow_state !== 'available') throw new Error('Course not found or not active')","typeGuard":"function isActiveCourse(c) { return c != null && c.id != null && c.workflow_state === 'available' }","tryCatchPattern":"try {\n  await client.mutate({ mutation: CREATE_COMMENT_BANK_ITEM, variables: { courseId } })\n} catch (e) {\n  if (e.message.includes('Course not found')) {\n    // re-resolve courseId from the context code before retrying\n  }\n  throw e\n}","preventionTips":["Derive courseId from a live context code each session, not cached values","Confirm the course is in 'available' (active) workflow state","Validate id formats before sending GraphQL variables","Check shard/root-account context resolves the course"],"tags":["graphql","mutation","course","record-not-found"],"backgroundTag":"entity-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"}