{"record":{"id":"80ce56adb8557583","repo":"instructure/canvas-lms","slug":"flashcards-response-missing-items","errorCode":null,"errorMessage":"Flashcards response missing items","messagePattern":"Flashcards response missing items","errorType":"exception","errorClass":"CedarUnavailable","httpStatus":null,"severity":"error","filePath":"app/services/study_assist.rb","lineNumber":306,"sourceCode":"    def build_quiz_response(raw)\n      items = parse_json_array!(raw)\n      raise CedarUnavailable, \"Quiz response missing items\" if items.blank? || !items.is_a?(Array)\n\n      quiz_items = items.first(10).map do |item|\n        question = item[:question] || item[\"question\"]\n        options = item[:options] || item[\"options\"]\n        result = item[:result] || item[\"result\"]\n        raise CedarUnavailable, \"Quiz item malformed\" if question.blank? || options.blank? || result.nil?\n\n        { question:, answers: options, correctAnswerIndex: result.to_i }\n      end\n\n      { quizItems: quiz_items }\n    end\n\n    def build_flashcards_response(raw)\n      cards = parse_json_array!(raw)\n      raise CedarUnavailable, \"Flashcards response missing items\" if cards.blank? || !cards.is_a?(Array)\n\n      flash_cards = cards.first(10).map do |card|\n        question = card[:question] || card[\"question\"]\n        answer = card[:answer] || card[\"answer\"]\n        raise CedarUnavailable, \"Flashcard item malformed\" if question.blank? || answer.blank?\n\n        { question:, answer: }\n      end\n\n      { flashCards: flash_cards }\n    end\n\n    def parse_json_array!(raw)\n      parsed = InstLLMHelper.extract_json_array(raw.to_s)\n      raise CedarUnavailable, \"Invalid JSON response from Cedar\" if parsed.nil?\n\n      parsed\n    end","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/study_assist.rb#L288-L324","documentation":"Guard in StudyAssist#build_flashcards_response: the Cedar/LLM flashcards response parsed to an empty value or a non-array, so CedarUnavailable is raised — the AI backend returned no usable flashcard items.","triggerScenarios":"build_response -> build_flashcards_response where parse_json_array! produced an empty array or a non-array value for the :flashcards tool.","commonSituations":"Short or non-prose source content (e.g. a single formula) giving the model nothing to cardify; the LLM answering in prose instead of JSON; output truncated by token limits so the array never materializes.","solutions":["Retry the flashcards generation — empty output is often transient.","Supply longer, more fact-dense source content.","Log raw Cedar output and verify extract_json_array can recover the array (check for truncation).","Tighten the flashcards prompt to require a non-empty JSON array of {question, answer}."],"exampleFix":"// before\n# assume cards always come back\n\n// after\ncards = service.call(tool: :flashcards) rescue nil\nretry_once if cards.nil? # transient empty LLM output","handlingStrategy":"retry","validationCode":null,"typeGuard":"def usable_card_array?(raw)\n  parsed = InstLLMHelper.extract_json_array(raw.to_s)\n  parsed.is_a?(Array) && parsed.any?\nend","tryCatchPattern":"begin\n  cards = service.call(tool: :flashcards)\nrescue StudyAssist::CedarUnavailable\n  cards = service.call(tool: :flashcards) # retry transient empty output\nend","preventionTips":["Use fact-dense source content so the model has material to cardify","Check for token-limit truncation when outputs are long","Keep the flashcards prompt strict about JSON-array output"],"tags":["llm","flashcards","empty-response","json"],"backgroundTag":"empty-result-set","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}