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
- Retry generation — a single malformed card usually disappears on a fresh call.
- Log the malformed card to identify which key the model actually used, then normalize that key in the mapping.
- Strengthen the prompt to specify exact JSON keys question and answer per card.
- 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
- Validate question/answer presence per card before rendering
- Normalize alternate answer keys (e.g. 'solution') if the model uses them
- Reduce requested card count if outputs get truncated
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Quiz item malformed
- Flashcards response missing items
- Invalid fields for a group
- Invalid fields for an outcome
- Invalid JSON response from Cedar
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)