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 (<...> 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
- Ensure the student submission contains actual prose outside of tags before invoking grading
- Pre-check in the caller: text.gsub(tag patterns, '').blank? and show 'submission is empty' instead of calling GradeService
- If legitimate text is being over-stripped, adjust the sanitize regexes so real content between </> pairs is preserved
Example fix
// before
result = GradeService.new(essay: submission.body, rubric:).call
// after
cleaned = submission.body.gsub(%r{<[^&]*>}, '')
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{<[^&]*>}, '').to_s
raise 'empty after sanitization' if stripped.strip.blank? Type guard
essay.present? && essay.gsub(/<[^&]*>/, '').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
- Require prose, not just embeds, in essay submissions
- Check for blank body before invoking AI grading
- Review sanitize regexes if real content gets stripped
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
- An unexpected error occurred while grading.
- Assignment Grade Error
- Can't update submission scores unless it's completed
- Peer review sub assignments cannot have a not_graded…
- Rubric criteria not descriptive enough
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 \< and \> (including the entities themselves)
text = text.gsub(%r{\\<[^&]*\\>.*?\\</[^&]*\\>}, "") # Remove content between encoded opening and closing tags
.gsub(/\\<[^&]*\\>/, "") # Remove any remaining encoded opening tags
.gsub(%r{\\</[^&]*\\>}, "") # 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)