instructure/canvas-lms · error · CedarUnavailable

Flashcard item malformed

Error message

Flashcard item malformed

What it means

Guard in StudyAssist#build_flashcards_response: a flashcard item in the parsed LLM response is missing question, options, or a result value, so CedarUnavailable 'Flashcard item malformed' is raised — the AI output didn't match the expected shape.

Solutions

  1. Retry generation — a single malformed card usually disappears on a fresh call.
  2. Log the malformed card to identify which key the model actually used, then normalize that key in the mapping.
  3. Strengthen the prompt to specify exact JSON keys question and answer per card.
  4. If truncation is the cause, reduce requested card count or increase output token limits.

Example fix

// before
cards.map { |c| { question: c[:question], answer: c[:answer] } }
# blank answers -> Flashcard item malformed

// after
cards.map { |c| { question: c[:question] || c['question'], answer: c[:answer] || c['answer'] || c['solution'] } }
Defensive patterns

Strategy: validation

Validate before calling

CARD_SCHEMA = ->(c) {
  q = c[:question] || c['question']
  a = c[:answer] || c['answer']
  q.present? && a.present?
}
pre_ok = parsed_cards.all?(&CARD_SCHEMA)

Type guard

def valid_card?(card)
  q = card[:question] || card['question']
  a = card[:answer] || card['answer']
  q.is_a?(String) && q.present? && a.is_a?(String) && a.present?
end

Try / catch

begin
  cards = service.call(tool: :flashcards)
rescue StudyAssist::CedarUnavailable
  Rails.logger.warn('flashcard item malformed; regenerating')
  cards = service.call(tool: :flashcards)
end

Prevention

When it happens

Trigger: build_response -> build_flashcards_response when any element of the parsed cards array lacks :question/:"question" or :answer/:"answer", or has a blank value for either.

Common situations: The LLM returns cards with only a question, or wraps answer text in a different key (e.g. 'solution'); model upgrades changing the emitted schema; truncation cutting off the last card's answer mid-JSON.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at app/services/study_assist.rb:311

        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 }
    end

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

      flash_cards = cards.first(10).map do |card|
        question = card[:question] || card["question"]
        answer = card[:answer] || card["answer"]
        raise CedarUnavailable, "Flashcard item malformed" if question.blank? || answer.blank?

        { question:, answer: }
      end

      { flashCards: flash_cards }
    end

    def parse_json_array!(raw)
      parsed = InstLLMHelper.extract_json_array(raw.to_s)
      raise CedarUnavailable, "Invalid JSON response from Cedar" if parsed.nil?

      parsed
    end
  end
end

View on GitHub (pinned to 1c9f0bb801)