{"record":{"id":"cce25d5e89f24994","repo":"instructure/canvas-lms","slug":"invalid-json-response-from-cedar","errorCode":null,"errorMessage":"Invalid JSON response from Cedar","messagePattern":"Invalid JSON response from Cedar","errorType":"exception","errorClass":"CedarUnavailable","httpStatus":null,"severity":"error","filePath":"app/services/study_assist.rb","lineNumber":321,"sourceCode":"\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\n  end\nend\n","sourceCodeStart":303,"sourceCodeEnd":327,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/study_assist.rb#L303-L327","documentation":"parse_json_array! runs the raw Cedar completion through InstLLMHelper.extract_json_array and raises CedarUnavailable 'Invalid JSON response from Cedar' when extraction returns nil — i.e. no JSON array could be recovered from the model's text at all.","triggerScenarios":"build_quiz_response or build_flashcards_response -> parse_json_array! where the raw completion contains no parseable JSON array (prose answer, fenced garbage, truncated JSON, or empty string).","commonSituations":"Model responding conversationally ('Here are your quiz questions: ...') with malformed or missing JSON; token-limit truncation cutting the array before the closing bracket; a Cedar/model change altering output format away from what extract_json_array expects.","solutions":["Retry the request — LLM JSON formatting failures are frequently transient.","Log the raw completion and test it against InstLLMHelper.extract_json_array to see why extraction fails.","Adjust the prompt to demand strict JSON-array output with no prose or markdown fences.","If the model's fences are the issue, verify the extract_json_array helper version supports them, or strip code fences before parsing."],"exampleFix":"// before\n# raw model text goes straight in\nitems = parse_json_array!(raw)\n\n// after\nstripped = raw.to_s.gsub(/```(json)?|```/, '').strip\nitems = InstLLMHelper.extract_json_array(stripped)\nraise StudyAssist::CedarUnavailable, 'Invalid JSON response from Cedar' if items.nil?","handlingStrategy":"try-catch","validationCode":"def recoverable_json?(raw)\n  !InstLLMHelper.extract_json_array(raw.to_s).nil?\nend","typeGuard":"def extractable_array?(raw)\n  InstLLMHelper.extract_json_array(raw.to_s).is_a?(Array)\nend","tryCatchPattern":"begin\n  parsed = parse_json_array!(raw)\nrescue StudyAssist::CedarUnavailable\n  stripped = raw.to_s.gsub(/```(json)?|```/, '').strip\n  parsed = InstLLMHelper.extract_json_array(stripped)\n  raise if parsed.nil?\nend","preventionTips":["Prompt the model for strict JSON arrays with no surrounding prose or code fences","Test raw completions against extract_json_array during prompt/model changes","Strip markdown fences before parsing as a standard pre-step"],"tags":["llm","json","parse-error","quiz","flashcards"],"backgroundTag":"json-parse-error","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"}