{"record":{"id":"446225e8886526b1","repo":"instructure/canvas-lms","slug":"endpoint-undefined","errorCode":null,"errorMessage":"endpoint undefined","messagePattern":"endpoint undefined","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/course.rb","lineNumber":2639,"sourceCode":"\n  def send_final_grades_to_endpoint(publishing_user, user_ids_to_publish = nil)\n    # actual grade publishing logic is here, but you probably want\n    # 'publish_final_grades'\n\n    recompute_student_scores_without_send_later(user_ids_to_publish)\n    enrollments = student_enrollments.not_fake.eager_load(:user).preload(:course_section).order_by_sortable_name\n    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 }","sourceCodeStart":2621,"sourceCodeEnd":2657,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/course.rb#L2621-L2657","documentation":"When publishing final grades, the grade_export plugin settings must include a publish_endpoint URL; if settings[:publish_endpoint] is blank, this error stops publishing because there is no SIS endpoint to post to.","triggerScenarios":"grade_export plugin enabled but its publish_endpoint setting is empty/blank when publish_final_grades runs (via its worker).","commonSituations":"Plugin enabled during setup but endpoint URL never configured; endpoint cleared during maintenance; copying plugin settings between environments without the endpoint.","solutions":["Set publish_endpoint in the grade_export plugin settings to the SIS grades URL","Check settings[:publish_endpoint].present? before invoking publishing","Verify plugin settings persist correctly in the target environment (DB vs YAML)"],"exampleFix":"// before\nplugin.settings # publish_endpoint: nil\n// after\nplugin_settings['publish_endpoint'] = 'https://sis.example.com/grades'\nplugin.save_settings","handlingStrategy":"validation","validationCode":"plugin = Canvas::Plugin.find!('grade_export')\nraise 'publish_endpoint not configured' if plugin.enabled? && plugin.settings[:publish_endpoint].blank?","typeGuard":"null","tryCatchPattern":"begin\n  course.publish_final_grades(user)\nrescue RuntimeError => e\n  raise unless e.message == 'endpoint undefined'\n  # guide admin to plugin settings to set the SIS endpoint\nend","preventionTips":["Configure publish_endpoint during plugin setup and verify it","Diff plugin settings when migrating environments","Add a settings health-check before scheduling publishing"],"tags":["ruby","rails","grades","config","sis"],"backgroundTag":"missing-required-config-field","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"}