instructure/canvas-lms · error
publishing disallowed for this publishing user
Error message
publishing disallowed for this publishing user
What it means
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.
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.
Example fix
// before Course.expire_all_bulk_course_publish_grades(course, admin) // after publishing_user = SisPseudonym.for(admin, course) ? admin : SisPseudonym.staff_with_pseudonym(course) course.publish_final_grades(publishing_user)
Defensive patterns
Strategy: validation
Validate before calling
raise 'no SIS pseudonym' if SisPseudonym.for(publishing_user, course).nil?
Type guard
is_sis_user = ->(u, c) { !SisPseudonym.for(u, c).nil? } Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- endpoint undefined
- A cross-listing referenced a non-existent section #
- A deleted cross-listing failed: #
- A new_id, '# ', referenced an existing # and the # with #…
- A new_integration_id, '#
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/3c6cbca183f87497.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/course.rb:2646
enrollments = enrollments.where(user_id: user_ids_to_publish) if user_ids_to_publish
errors = []
posts_to_make = []
posted_enrollment_ids = []
all_enrollment_ids = enrollments.map(&:id)
begin
raise "final grade publishing disabled" unless Canvas::Plugin.find!("grade_export").enabled?
settings = Canvas::Plugin.find!("grade_export").settings
raise "endpoint undefined" if settings[:publish_endpoint].blank?
format_settings = Course.valid_grade_export_types[settings[:format_type]]
raise "unknown format type: #{settings[:format_type]}" unless format_settings
raise "grade publishing requires a grading standard" if !grading_standard_enabled? && format_settings[:requires_grading_standard]
publishing_pseudonym = SisPseudonym.for(publishing_user, self)
raise "publishing disallowed for this publishing user" if publishing_pseudonym.nil? && format_settings[:requires_publishing_pseudonym]
callback = Course.valid_grade_export_types[settings[:format_type]][:callback]
posts_to_make = callback.call(self, enrollments, publishing_user, publishing_pseudonym)
rescue => e
Enrollment.where(id: all_enrollment_ids).update_all(grade_publishing_status: "error", grade_publishing_message: e.to_s)
raise e
end
default_timeout = Setting.get("send_final_grades_to_endpoint_timelimit", 15.seconds.to_s).to_f
timeout_options = { raise_on_timeout: true, fallback_timeout_length: default_timeout }
posts_to_make.each do |enrollment_ids, res, mime_type, headers = {}|
posted_enrollment_ids += enrollment_ids
if res
Canvas.timeout_protection("send_final_grades_to_endpoint:#{global_root_account_id}", timeout_options) do
SSLCommon.post_data(settings[:publish_endpoint], res, mime_type, headers)
endView on GitHub (pinned to 1c9f0bb801)