{"record":{"id":"e2d99ede29e6b513","repo":"instructure/canvas-lms","slug":"missing-required-asset-parameter-asset-param-name-lti","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_submission_notice_builder.rb","lineNumber":48,"sourceCode":"        for_user_id\n        notice_event_timestamp\n        submission_lti_id\n      ].freeze\n      REQUIRED_ASSETS_PARAMS = %i[asset_id url sha256_checksum timestamp size content_type].freeze\n\n      def initialize(params)\n        validate_params!(params)\n        @params = params\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      end\n\n      def notice_type\n        Lti::Pns::NoticeTypes::ASSET_PROCESSOR_SUBMISSION\n      end\n\n      def custom_ims_claims(_tool)\n        {\n          for_user: {\n            user_id: @params[:for_user_id],\n          },\n          assetreport: {\n            scope: [\n              TokenScopes::LTI_ASSET_REPORT_SCOPE\n            ],\n            report_url: @params[:asset_report_service_url]","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/lti/pns/lti_asset_processor_submission_notice_builder.rb#L30-L66","documentation":"After validating top-level params, validate_params! iterates params[:assets] and checks each asset hash for every key in REQUIRED_ASSETS_PARAMS, raising ArgumentError naming the missing asset parameter. It ensures every asset in an LTI Asset Processor Submission PNS notice carries the fields needed to render the notification.","triggerScenarios":"Constructing the builder where one or more hashes inside params[:assets] lack a key from REQUIRED_ASSETS_PARAMS (e.g. an asset missing :asset_id or :title), typically when asset records were partially loaded or serialized incompletely.","commonSituations":"Batch submission events where some assets were deleted between event capture and notice build; serialization code that only includes asset attributes conditionally; hand-built test payloads missing per-asset fields.","solutions":["Read REQUIRED_ASSETS_PARAMS and ensure every hash in params[:assets] contains all of those keys.","Filter out incomplete assets before building: assets.select { |a| REQUIRED keys present } or reload the missing data.","Log/inspect the offending asset to find which pipeline stage dropped the field.","Add a caller-side validation that mirrors the builder's checks to fail earlier with better context."],"exampleFix":"// before\nLti::Pns::LtiAssetProcessorSubmissionNoticeBuilder.new(course:, assets: submissions.map { |s| { asset_id: s.id } })\n// after\nassets = submissions.map { |s| { asset_id: s.id, title: s.title } }\nLti::Pns::LtiAssetProcessorSubmissionNoticeBuilder.new(course:, assets:)","handlingStrategy":"validation","validationCode":"req = Lti::Pns::LtiAssetProcessorSubmissionNoticeBuilder::REQUIRED_ASSETS_PARAMS\nbad = params[:assets].reject { |a| req.all? { |k| a[k].present? } }\nraise ArgumentError, \"incomplete assets: #{bad.inspect}\" if bad.any?","typeGuard":null,"tryCatchPattern":"begin\n  builder = Lti::Pns::LtiAssetProcessorSubmissionNoticeBuilder.new(params)\nrescue ArgumentError => e\n  Canvas::Errors.capture_exception(:lti_pns, e, :warn)\nend","preventionTips":["Validate each asset hash against REQUIRED_ASSETS_PARAMS before batching","Reload assets from the DB at notice-build time instead of trusting serialized snapshots","Log the offending asset when filtering out incomplete ones"],"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"}