{"record":{"id":"02efaffd74785fc2","repo":"instructure/canvas-lms","slug":"missing-required-parameter-course","errorCode":null,"errorMessage":"Missing required parameter: course","messagePattern":"Missing required parameter: course","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"app/models/lti/pns/lti_context_copy_notice_builder.rb","lineNumber":26,"sourceCode":"# Canvas is free software: you can redistribute it and/or modify it under\n# the terms of the GNU Affero General Public License as published by the Free\n# Software Foundation, version 3 of the License.\n#\n# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY\n# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more\n# details.\n#\n# You should have received a copy of the GNU Affero General Public License along\n# with this program. If not, see <http://www.gnu.org/licenses/>.\n\nmodule Lti\n  module Pns\n    class LtiContextCopyNoticeBuilder < NoticeBuilder\n      attr_reader :params\n\n      def initialize(params = {})\n        raise ArgumentError, \"Missing required parameter: course\" unless params[:course]\n        raise ArgumentError, \"Missing required parameter: copied_at\" unless params[:copied_at]\n\n        @params = params\n        super()\n      end\n\n      def notice_type\n        Lti::Pns::NoticeTypes::CONTEXT_COPY\n      end\n\n      def custom_instructure_claims(_tool)\n        {}\n      end\n\n      def custom_ims_claims(_tool)\n        course = params[:course]\n\n        context = {","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/lti/pns/lti_context_copy_notice_builder.rb#L8-L44","documentation":"Lti::Pns::LtiContextCopyNoticeBuilder's initialize requires params[:course]; if absent it raises ArgumentError \"Missing required parameter: course\". A context-copy PNS notice cannot be rendered without knowing which course was copied, so the builder fails fast at construction time.","triggerScenarios":"Calling Lti::Pns::LtiContextCopyNoticeBuilder.new without a :course key, or with course: nil — e.g. when the copy job runs after the source/destination course lookup returned nil or the course was deleted.","commonSituations":"Course copy/import jobs enqueued before a hard-delete removed the course; passing the wrong hash (e.g. the job options hash) as params; key renames like course_id vs course.","solutions":["Pass the loaded Course object (or whatever type :course expects) as params[:course] when constructing the builder.","Verify the course still exists before scheduling/building the notice; skip the notice if it was deleted.","Check the caller is passing the Course record, not just a course id, under the :course key.","Wrap construction in a check: build the notice only if params[:course].present?."],"exampleFix":"// before\nLti::Pns::LtiContextCopyNoticeBuilder.new({ copied_at: time })\n// after\nLti::Pns::LtiContextCopyNoticeBuilder.new({ course: course, copied_at: time })","handlingStrategy":"validation","validationCode":"return nil unless params[:course].present?\nLti::Pns::LtiContextCopyNoticeBuilder.new(params)","typeGuard":null,"tryCatchPattern":"begin\n  Lti::Pns::LtiContextCopyNoticeBuilder.new(params)\nrescue ArgumentError => e\n  Rails.logger.warn(\"context copy notice skipped: #{e.message}\")\nend","preventionTips":["Check course&.persisted? before building the notice","Skip notification building when the course was deleted mid-job","Keep a single hash-shape constant shared between job and builder"],"tags":["ruby","lti","argument-error","validation"],"backgroundTag":"missing-required-argument","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"}