instructure/canvas-lms · error

content is not a Submission

Error message

content is not a Submission

What it means

ContentParticipation.submission_read_state requires its content argument to be a Submission. Raising "content is not a Submission" protects the participation-read-state lookup, which queries ContentParticipation scoped by content.

Solutions

  1. Pass the Submission record (e.g. assignment.submissions.find_by(user:)) instead of the assignment or discussion entry
  2. Guard the call site so only Submission instances reach submission_read_state
  3. If tracking participation for other content types, use the appropriate participation class for that type

Example fix

# before
ContentParticipation.submission_read_state(content: assignment, user: student)
# after
submission = assignment.submissions.find_by!(user: student)
ContentParticipation.submission_read_state(content: submission, user: student)
Defensive patterns

Strategy: type-guard

Validate before calling

raise 'expected Submission' unless content.is_a?(Submission)

Type guard

def submission?(obj) = obj.is_a?(Submission)

Try / catch

begin
  ContentParticipation.submission_read_state(content:, user:)
rescue RuntimeError => e
  raise e unless e.message == 'content is not a Submission'
  Rails.logger.warn('submission_read_state called with non-Submission')
end

Prevention

When it happens

Trigger: Calling submission_read_state (or submission_item_read?) with a DiscussionEntry, Assignment, or other non-Submission object as content:; passing a nil content; refactor renamed parameters and callers pass the wrong record.

Common situations: Generic read-state code reused for multiple content types and passed the wrong model; seed/data-fix scripts passing an assignment instead of its submission; view/controller passing the wrong association (submission vs submission.history etc.).

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at app/models/content_participation.rb:146

    content.content_participations.build(user:, workflow_state:, content_item:)
  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|

View on GitHub (pinned to 1c9f0bb801)