{"record":{"id":"ebff7afa3f75d045","repo":"instructure/canvas-lms","slug":"missing-required-asset-parameter-asset-param-name","errorCode":null,"errorMessage":"Missing required asset parameter #{asset_param_name}","messagePattern":"Missing required asset parameter #(.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"app/models/lti/pns/lti_asset_processor_contribution_notice_builder.rb","lineNumber":58,"sourceCode":"        SUBMITTED,\n        DELETED,\n        HIDDEN\n      ].freeze\n\n      def initialize(params)\n        validate_params!(params)\n        @params = params\n        ensure_lti_ids\n        super()\n      end\n\n      def validate_params!(params)\n        REQUIRED_PARAMS.each do |param_name|\n          raise ArgumentError, \"Missing required parameter: #{param_name}\" unless params[param_name]\n        end\n        params[:assets].each do |asset|\n          REQUIRED_ASSETS_PARAMS.each do |asset_param_name|\n            raise ArgumentError, \"Missing required asset parameter #{asset_param_name}\" unless asset[asset_param_name]\n          end\n        end\n        if params[:assets].any? && params[:asset_report_service_url].blank?\n          raise ArgumentError, \"Missing required parameter: asset_report_service_url when assets are present\"\n        end\n        unless VALID_CONTRIBUTION_STATUSES.include?(params[:contribution_status])\n          raise ArgumentError, \"Invalid contribution_status: #{params[:contribution_status]}. Must be one of: #{VALID_CONTRIBUTION_STATUSES.join(\", \")}\"\n        end\n      end\n\n      def notice_type\n        NoticeTypes::ASSET_PROCESSOR_CONTRIBUTION\n      end\n\n      def custom_ims_claims(_tool)\n        discussion_entry_version = @params[:discussion_entry_version]\n        discussion_entry = discussion_entry_version.discussion_entry\n        {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/lti/pns/lti_asset_processor_contribution_notice_builder.rb#L40-L76","documentation":"Guard in Lti::Pns::LtiAssetProcessorContributionNoticeBuilder#validate_params!: an entry in params[:assets] is missing one of the REQUIRED_ASSETS_PARAMS keys, so ArgumentError is raised while constructing a Caliper/asset-processor notification. It's a constructor-time input validation for the notice payload.","triggerScenarios":"Passing params[:assets] where at least one asset hash is missing one of the REQUIRED_ASSETS_PARAMS keys (or holds nil/false).","commonSituations":"Building asset arrays from partial records (some assets lack the field); symbol vs string key mismatches in the asset hashes; schema drift between producer and builder.","solutions":["Ensure every entry in params[:assets] contains all REQUIRED_ASSETS_PARAMS keys with truthy values","Normalize asset hashes (symbolize keys, default-fill known values) before constructing the builder","Filter out assets missing required fields upstream, or validate the array before calling initialize"],"exampleFix":"// before\nparams[:assets] = [{ id: 1 }] # missing other required asset params\n// after\nreq = Lti::Pns::LtiAssetProcessorContributionNoticeBuilder::REQUIRED_ASSETS_PARAMS\nparams[:assets] = raw_assets.select { |a| req.all? { |k| a[k] } }","handlingStrategy":"validation","validationCode":"req = Lti::Pns::LtiAssetProcessorContributionNoticeBuilder::REQUIRED_ASSETS_PARAMS\nbad = params[:assets].to_a.reject { |a| req.all? { |k| a[k] } }\nraise \"invalid assets\" unless bad.empty?","typeGuard":"def valid_asset?(a) = Lti::Pns::LtiAssetProcessorContributionNoticeBuilder::REQUIRED_ASSETS_PARAMS.all? { |k| a[k] }","tryCatchPattern":"begin\n  Builder.new(params)\nrescue ArgumentError => e\n  Rails.logger.error(\"PNS builder: #{e.message}\")\nend","preventionTips":["Sanitize each asset hash against REQUIRED_ASSETS_PARAMS","Symbolize keys before validation","Drop or repair incomplete assets upstream"],"tags":["lti","params-validation","push-notification"],"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"}