instructure/canvas-lms · error · ArgumentError
Invalid contribution_status: #
Error message
Invalid contribution_status: #{params[:contribution_status]}. Must be one of: #{VALID_CONTRIBUTION_STATUSES.join(", ")} What it means
Guard in Lti::Pns::LtiAssetProcessorContributionNoticeBuilder#validate_params!: params[:contribution_status] is not one of the allowed values (SUBMITTED, DELETED, HIDDEN), so ArgumentError is raised while building the LTI asset processor contribution notice.
Solutions
- Set contribution_status to one of the values listed in VALID_CONTRIBUTION_STATUSES
- Normalize/map upstream status values to the accepted enum before building the notice
- Rescue ArgumentError and log the invalid value for schema-drift follow-up
Example fix
// before
{ contribution_status: "DONE" }
// after
VALID = Lti::Pns::LtiAssetProcessorContributionNoticeBuilder::VALID_CONTRIBUTION_STATUSES
status = upstream_status.downcase
raise "unknown status" unless VALID.include?(status)
params[:contribution_status] = status Defensive patterns
Strategy: validation
Validate before calling
valid = Lti::Pns::LtiAssetProcessorContributionNoticeBuilder::VALID_CONTRIBUTION_STATUSES raise "bad status" unless valid.include?(params[:contribution_status])
Type guard
def valid_status?(s) = Lti::Pns::LtiAssetProcessorContributionNoticeBuilder::VALID_CONTRIBUTION_STATUSES.include?(s)
Try / catch
begin
Builder.new(params)
rescue ArgumentError => e
Rails.logger.error("PNS builder: #{e.message}")
end Prevention
- Map upstream statuses to the accepted enum
- Keep VALID_CONTRIBUTION_STATUSES in sync with producers
- Normalize case/type before passing status
When it happens
Trigger: Passing a contribution_status string not in VALID_CONTRIBUTION_STATUSES (typo, wrong case, or new upstream value the builder doesn't know).
Common situations: Upstream asset processor service emitting a new status value; callers passing symbols instead of expected strings or vice versa; typos like 'complted'.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Missing required asset parameter #
- Missing required parameter: asset_report_service_url when…
- Missing required parameter: #
- Validation failed: Notice type unknown, must be one of
- Access token expired
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/00e2c680d9b569a3.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/lti/pns/lti_asset_processor_contribution_notice_builder.rb:65
@params = params
ensure_lti_ids
super()
end
def validate_params!(params)
REQUIRED_PARAMS.each do |param_name|
raise ArgumentError, "Missing required parameter: #{param_name}" unless params[param_name]
end
params[:assets].each do |asset|
REQUIRED_ASSETS_PARAMS.each do |asset_param_name|
raise ArgumentError, "Missing required asset parameter #{asset_param_name}" unless asset[asset_param_name]
end
end
if params[:assets].any? && params[:asset_report_service_url].blank?
raise ArgumentError, "Missing required parameter: asset_report_service_url when assets are present"
end
unless VALID_CONTRIBUTION_STATUSES.include?(params[:contribution_status])
raise ArgumentError, "Invalid contribution_status: #{params[:contribution_status]}. Must be one of: #{VALID_CONTRIBUTION_STATUSES.join(", ")}"
end
end
def notice_type
NoticeTypes::ASSET_PROCESSOR_CONTRIBUTION
end
def custom_ims_claims(_tool)
discussion_entry_version = @params[:discussion_entry_version]
discussion_entry = discussion_entry_version.discussion_entry
{
for_user: {
user_id: @params[:for_user_id],
},
assetreport: @params[:asset_report_service_url]&.then do |url|
{
scope: [
TokenScopes::LTI_ASSET_REPORT_SCOPEView on GitHub (pinned to 1c9f0bb801)