{"record":{"id":"9b55df8eb9ac724d","repo":"instructure/canvas-lms","slug":"context-must-be-of-type-klass","errorCode":null,"errorMessage":"Context must be of type '#{klass}'","messagePattern":"Context must be of type '#(.+?)'","errorType":"exception","errorClass":"ActiveRecord::RecordNotFound","httpStatus":404,"severity":"error","filePath":"app/controllers/application_controller.rb","lineNumber":1479,"sourceCode":"  end\n\n  def require_context_and_read_access\n    require_context && authorized_action(@context, @current_user, :read)\n  end\n\n  helper_method :clean_return_to\n\n  def require_account_context\n    require_context_type(Account)\n  end\n\n  def require_course_context\n    require_context_type(Course)\n  end\n\n  def require_context_type(klass)\n    unless require_context && @context.is_a?(klass)\n      raise ActiveRecord::RecordNotFound, \"Context must be of type '#{klass}'\"\n    end\n\n    true\n  end\n\n  MAX_ACCOUNT_LINEAGE_TO_SHOW_IN_CRUMBS = 3\n\n  # Can be used as a before_action, or just called from controller code.\n  # Assigns the variable @context to whatever context the url is scoped\n  # to.  So /courses/5/assignments would have a @context=Course.find(5).\n  # Also assigns @context_membership to the membership type of @current_user\n  # if @current_user is a member of the context.\n  def get_context(user_scope: nil)\n    GuardRail.activate(:secondary) do\n      unless @context\n        if params[:course_id]\n          course_scope = @token ? Course : Course.active\n          @context = api_find(course_scope, params[:course_id])","sourceCodeStart":1461,"sourceCodeEnd":1497,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/controllers/application_controller.rb#L1461-L1497","documentation":"process_user_observer raises this ImportError when student_id equals observer_id — a user cannot be their own observer. The check is a plain string equality on the two SIS IDs, done before any DB lookups, so even identical IDs that resolve to no user still raise this rather than a not-found error.","triggerScenarios":"A users_observers CSV row where the observer_id and user_id columns contain the same SIS ID (copy-paste error); calling process_user_observer('X', 'X', 'active') programmatically.","commonSituations":"Spreadsheet fill-down copying the student's own ID into the observer column; bulk scripts that compute observer_id from the same variable as student_id; parents provisioned with a single SIS ID used for both roles.","solutions":["Correct the observer_id in the row to a different user's SIS ID.","Filter self-pairings out in the pre-import validation step.","If self-observation was intended as a no-op, omit the row entirely."],"exampleFix":"// before (CSV)\nuser_id,observer_id\nSTU001,STU001\n\n// after\nuser_id,observer_id\nSTU001,OBS009","handlingStrategy":"validation","validationCode":"raise 'observer cannot observe themselves' if observer_id.to_s == student_id.to_s","typeGuard":null,"tryCatchPattern":"begin\n  importer.process_user_observer(obs_id, stu_id, status)\nrescue SIS::Import::ImportError => e\n  logger.warn(\"skipped row: #{e.message}\")\nend","preventionTips":["Assert observer_id != student_id in your ETL pipeline.","Watch for copy/fill-down errors in spreadsheets when generating CSVs.","Use distinct variables/sources for observer and student IDs in scripts."],"tags":["sis-import","ruby","invalid-value","user-observer"],"backgroundTag":"invalid-argument-value","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"}