instructure/canvas-lms · error · CedarUnavailable

Summary response missing

Error message

Summary response missing

What it means

Guard in StudyAssist#build_summarize_response: the LLM/Cedar tool returned an empty or blank summary string for the summarize request, so CedarUnavailable is raised to signal the AI backend produced no usable content.

Solutions

  1. Re-run the summarize request — an empty completion is often transient.
  2. Check the input content: extremely short or empty source text may produce nothing; ensure resolve_content returned real text.
  3. Inspect prompt_for_cedar substitution for :summarize (KIND) to confirm the prompt is well-formed.
  4. Check Cedar-side logs for completion truncation or content filtering of the response.

Example fix

// before
# no check on content before summarizing
summary = service.call(tool: :summarize)

// after
raise StudyAssist::ContentUnavailable, 'Nothing to summarize' if content.text.blank?
summary = service.call(tool: :summarize)
Defensive patterns

Strategy: retry

Validate before calling

raise ArgumentError, 'nothing to summarize' if content.text.strip.blank?

Try / catch

begin
  summary = service.call(tool: :summarize)
rescue StudyAssist::CedarUnavailable
  summary = retry_with_backoff(tries: 2) { service.call(tool: :summarize) }
end

Prevention

When it happens

Trigger: build_response -> build_summarize_response when raw (the Cedar completion for the :summarize tool) is nil, empty, or whitespace-only.

Common situations: Cedar silently returning an empty completion (e.g. content too short to summarize, prompt substitution issue for the KIND placeholder, or the model returning a refusal-only/empty body).

Related errors


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

Appendix: source

Thrown at app/services/study_assist.rb:283

    def summarize_kind(content)
      case content.kind
      when :page then "page"
      when :file then "file"
      else "material"
      end
    end

    def build_response(tool_key, raw)
      case tool_key
      when :summarize then build_summarize_response(raw)
      when :quiz then build_quiz_response(raw)
      when :flashcards then build_flashcards_response(raw)
      end
    end

    def build_summarize_response(raw)
      text = raw.to_s.strip
      raise CedarUnavailable, "Summary response missing" if text.blank?

      { response: text }
    end

    def build_quiz_response(raw)
      items = parse_json_array!(raw)
      raise CedarUnavailable, "Quiz response missing items" if items.blank? || !items.is_a?(Array)

      quiz_items = items.first(10).map do |item|
        question = item[:question] || item["question"]
        options = item[:options] || item["options"]
        result = item[:result] || item["result"]
        raise CedarUnavailable, "Quiz item malformed" if question.blank? || options.blank? || result.nil?

        { question:, answers: options, correctAnswerIndex: result.to_i }
      end

      { quizItems: quiz_items }

View on GitHub (pinned to 1c9f0bb801)