instructure/canvas-lms · error

No essay submission found after removing text between <>

Error message

No essay submission found after removing text between <>

What it means

GradeService#sanitize_essay strips encoded tag-like content (&lt;...&gt; and matching closing tags) from the submitted essay. If nothing remains after this stripping, there is no real essay text to grade, so the service raises instead of sending an empty submission to the Cedar grading engine. This catches essays composed almost entirely of markup or placeholder tags.

Solutions

  1. Ensure the student submission contains actual prose outside of tags before invoking grading
  2. Pre-check in the caller: text.gsub(tag patterns, '').blank? and show 'submission is empty' instead of calling GradeService
  3. If legitimate text is being over-stripped, adjust the sanitize regexes so real content between &lt;/&gt; pairs is preserved

Example fix

// before
result = GradeService.new(essay: submission.body, rubric:).call
// after
cleaned = submission.body.gsub(%r{&lt;[^&]*&gt;}, '')
raise I18n.t('submission is empty') if cleaned.strip.split.size < 5
result = GradeService.new(essay: submission.body, rubric:).call
Defensive patterns

Strategy: validation

Validate before calling

stripped = essay.gsub(%r{&lt;[^&]*&gt;}, '').to_s
raise 'empty after sanitization' if stripped.strip.blank?

Type guard

essay.present? && essay.gsub(/&lt;[^&]*&gt;/, '').strip.present?

Try / catch

begin
  GradeService.new(essay:, rubric:, rubric_association:).call
rescue RuntimeError => e
  show_empty_submission_error if e.message.include?('No essay submission')
end

Prevention

When it happens

Trigger: An essay submission whose body consists only of HTML/tag-like content between angle brackets (or their HTML-encoded entities), which the gsub patterns remove entirely; submissions embedding only embedded-object tags (e.g. media embeds) with no prose; empty or whitespace-only submissions after tag removal.

Common situations: Students pasting rich-text embeds instead of writing text; submissions from tools that serialize content purely as tags; corrupted submissions where the text entry failed to save.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at app/services/grade_service.rb:148

    end
  end

  def sanitize_essay(text)
    # First decode any HTML entities
    text = CGI.unescapeHTML(text)
    text = ActionView::Base.full_sanitizer.sanitize(text)

    # Remove any remaining HTML tags and their content
    text = text.gsub(%r{<[^>]*>.*?</[^>]*>}, "")                          # Remove content between other opening and closing tags
               .gsub(/<[^>]*>/, "")                                       # Remove any remaining opening tags
               .gsub(%r{</[^>]*>}, "")                                    # Remove any remaining closing tags

    # Remove any content between \&lt; and \&gt; (including the entities themselves)
    text = text.gsub(%r{\\&lt;[^&]*\\&gt;.*?\\&lt;/[^&]*\\&gt;}, "")      # Remove content between encoded opening and closing tags
               .gsub(/\\&lt;[^&]*\\&gt;/, "")                             # Remove any remaining encoded opening tags
               .gsub(%r{\\&lt;/[^&]*\\&gt;}, "")                          # Remove any remaining encoded closing tags

    raise "No essay submission found after removing text between <>" if text.blank?

    # Remove lines starting with more than 3 # characters
    text = text.split("\n").reject { |line| line.strip.start_with?("####") }.join("\n")

    # Clean up any resulting double spaces and trim
    text.gsub(/\s+/, " ").strip
  end

  def validate_essay_length(text)
    raise "Submission must be at least 5 words long" if text.split.size < 5
  end

  def friendly_cedar_error_for(error)
    case error
    when InstructureMiscPlugin::Extensions::CedarClient::CedarLimitReachedError
      I18n.t("Grading is temporarily unavailable. Please try again later.")
    when InstructureMiscPlugin::Extensions::CedarClient::ContentTooLongError
      I18n.t("The submission is too long to be graded automatically.")

View on GitHub (pinned to 1c9f0bb801)