{"record":{"id":"3c6cbca183f87497","repo":"instructure/canvas-lms","slug":"publishing-disallowed-for-this-publishing-user","errorCode":null,"errorMessage":"publishing disallowed for this publishing user","messagePattern":"publishing disallowed for this publishing user","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/course.rb","lineNumber":2646,"sourceCode":"    enrollments = enrollments.where(user_id: user_ids_to_publish) if user_ids_to_publish\n\n    errors = []\n    posts_to_make = []\n    posted_enrollment_ids = []\n    all_enrollment_ids = enrollments.map(&:id)\n\n    begin\n      raise \"final grade publishing disabled\" unless Canvas::Plugin.find!(\"grade_export\").enabled?\n\n      settings = Canvas::Plugin.find!(\"grade_export\").settings\n      raise \"endpoint undefined\" if settings[:publish_endpoint].blank?\n\n      format_settings = Course.valid_grade_export_types[settings[:format_type]]\n      raise \"unknown format type: #{settings[:format_type]}\" unless format_settings\n      raise \"grade publishing requires a grading standard\" if !grading_standard_enabled? && format_settings[:requires_grading_standard]\n\n      publishing_pseudonym = SisPseudonym.for(publishing_user, self)\n      raise \"publishing disallowed for this publishing user\" if publishing_pseudonym.nil? && format_settings[:requires_publishing_pseudonym]\n\n      callback = Course.valid_grade_export_types[settings[:format_type]][:callback]\n      posts_to_make = callback.call(self, enrollments, publishing_user, publishing_pseudonym)\n    rescue => e\n      Enrollment.where(id: all_enrollment_ids).update_all(grade_publishing_status: \"error\", grade_publishing_message: e.to_s)\n      raise e\n    end\n\n    default_timeout = Setting.get(\"send_final_grades_to_endpoint_timelimit\", 15.seconds.to_s).to_f\n\n    timeout_options = { raise_on_timeout: true, fallback_timeout_length: default_timeout }\n\n    posts_to_make.each do |enrollment_ids, res, mime_type, headers = {}|\n      posted_enrollment_ids += enrollment_ids\n      if res\n        Canvas.timeout_protection(\"send_final_grades_to_endpoint:#{global_root_account_id}\", timeout_options) do\n          SSLCommon.post_data(settings[:publish_endpoint], res, mime_type, headers)\n        end","sourceCodeStart":2628,"sourceCodeEnd":2664,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/course.rb#L2628-L2664","documentation":"Course#publish_final_grades raises this when the grade-export format requires a publishing pseudonym (an SIS-linked login for the publishing user) but SisPseudonym.for returns nil for that user in this course's root account. The format's :requires_publishing_pseudonym flag demands that the person posting grades be traceable to the SIS; without a pseudonym the publish is aborted.","triggerScenarios":"Calling publish_final_grades (or its async wrapper) with settings[:format_type] whose Course.valid_grade_export_types entry has requires_publishing_pseudonym: true, where the publishing_user has no SIS pseudonym in the root account (SisPseudonym.for(publishing_user, self) is nil).","commonSituations":"Admin without an SIS-imported login publishing grades; pseudonym soft-deleted or on the wrong shard/account; SIS integration not configured for the root account so no sis_pseudonyms exist; using a non-SIS user (e.g. a support admin) as the publishing user.","solutions":["Choose a publishing_user that has an active SIS pseudonym in the course's root account (verify via SisPseudonym.for(user, course)).","Run a SIS import that provisions the user's login, or add sis_user_id to their pseudonym, then retry.","Pick a grade export format that does not set requires_publishing_pseudonym, or remove that flag from the registered export type.","Confirm the root_account_id/shard is correct so SisPseudonym lookup isn't hitting the wrong account."],"exampleFix":"// before\nCourse.expire_all_bulk_course_publish_grades(course, admin)\n\n// after\npublishing_user = SisPseudonym.for(admin, course) ? admin : SisPseudonym.staff_with_pseudonym(course)\ncourse.publish_final_grades(publishing_user)","handlingStrategy":"validation","validationCode":"raise 'no SIS pseudonym' if SisPseudonym.for(publishing_user, course).nil?","typeGuard":"is_sis_user = ->(u, c) { !SisPseudonym.for(u, c).nil? }","tryCatchPattern":null,"preventionTips":["Pre-select publishing users from the SIS-linked admins list","Provision SIS logins before enabling grade publishing","Assert requires_publishing_pseudonym formats have SIS-backed users in tests"],"tags":["ruby-on-rails","sis","grades","pseudonym"],"backgroundTag":"missing-credentials","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"}