instructure/canvas-lms · error

# is invalid

Error message

#{content_item} is invalid

What it means

submission_read_state validates the optional content_item against the CONTENT_ITEMS allowlist and raises "#{content_item} is invalid" for any value not in the list, since it is used directly as a query column.

Solutions

  1. Pass one of the exact values in ContentParticipation::CONTENT_ITEMS
  2. Normalize/whitelist user-supplied content_item params before the call
  3. Check CONTENT_ITEMS in this file to confirm the current allowed keys

Example fix

# before
ContentParticipation.submission_item_read?(content:, user:, content_item: params[:item].to_sym)
# after
item = params[:item].to_sym
ContentParticipation.submission_item_read?(content:, user:, content_item: item) if ContentParticipation::CONTENT_ITEMS.include?(item)
Defensive patterns

Strategy: validation

Validate before calling

item = params[:content_item]&.to_sym
raise 'invalid content_item' unless item.nil? || ContentParticipation::CONTENT_ITEMS.include?(item)

Type guard

valid_item = ->(v) { v.nil? || ContentParticipation::CONTENT_ITEMS.include?(v) }

Try / catch

begin
  ContentParticipation.submission_item_read?(content:, user:, content_item: item)
rescue RuntimeError => e
  raise e unless e.message.end_with?('is invalid')
  # fall back to whole-content read state without content_item
end

Prevention

When it happens

Trigger: Calling submission_read_state/submission_item_read? with a content_item symbol/string not in ContentParticipation::CONTENT_ITEMS (typo, wrong enum value, atom key from an API payload).

Common situations: Clients pass arbitrary content_item values from request params; enum renamed in a refactor so old callers pass obsolete keys; symbol vs string mismatch not normalized before the allowlist check.

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


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/39a4716dd056a2a1. Report an issue: GitHub.

Appendix: source

Thrown at app/models/content_participation.rb:147

  end
  private_class_method :build_item

  def self.same_workflow_state?(participant, workflow_state)
    participant.present? && participant.workflow_state == workflow_state
  end
  private_class_method :same_workflow_state?

  def self.submission_read?(content:, user:)
    submission_read_state(content, user) != "unread"
  end

  def self.submission_item_read?(content:, user:, content_item:)
    submission_read_state(content, user, content_item) != "unread"
  end

  def self.submission_read_state(content, user, content_item = nil)
    raise "content is not a Submission" unless content.is_a?(Submission)
    raise "#{content_item} is invalid" if content_item.present? && !CONTENT_ITEMS.include?(content_item)

    states = if content_item.present?
               ContentParticipation.where(content:, user:, content_item:).pluck(:workflow_state)
             else
               ContentParticipation.where(content:, user:).pluck(:workflow_state)
             end

    return nil if states.empty?
    return "unread" if states.any?("unread")

    "read"
  end

  def self.items_by_submission(participations, workflow_state)
    unread_items = {}

    participations.each do |cp|
      unread_items[cp.content_id] ||= []

View on GitHub (pinned to 1c9f0bb801)