{"record":{"id":"700d767721b6ed43","repo":"instructure/canvas-lms","slug":"invalid-json-response-could-not-extract-valid-json-array","errorCode":null,"errorMessage":"Invalid JSON response: could not extract valid JSON array","messagePattern":"Invalid JSON response: could not extract valid JSON array","errorType":"exception","errorClass":"CedarAi::Errors::GraderError","httpStatus":null,"severity":"error","filePath":"app/helpers/json_utils_helper.rb","lineNumber":37,"sourceCode":"#\n\nmodule JsonUtilsHelper\n  def safe_parse_json_array(response)\n    return [] if response.blank?\n\n    begin\n      parsed = JSON.parse(response)\n      return parsed.is_a?(Array) ? parsed : []\n    rescue JSON::ParserError\n      if response.include?(\"[\") && response.include?(\"]\")\n        json_like = response[response.index(\"[\"), response.rindex(\"]\") - response.index(\"[\") + 1]\n        repaired = escape_inner_quotes(json_like)\n\n        begin\n          parsed = JSON.parse(repaired)\n          return parsed.is_a?(Array) ? parsed : []\n        rescue JSON::ParserError\n          raise CedarAi::Errors::GraderError, \"Invalid JSON response: could not extract valid JSON array\"\n        end\n      end\n    end\n\n    raise CedarAi::Errors::GraderError, \"Invalid JSON response: could not extract valid JSON array\"\n  end\n\n  def escape_inner_quotes(json_str)\n    result = json_str.dup\n    key_start_regex = /\"([^\"\\\\]*)\"\\s*:\\s*\"/\n\n    pos = 0\n    while (m = key_start_regex.match(result, pos))\n      value_start = m.end(0)\n      closing_regex = /\"(?=\\s*(?:,|\\}|\\]))/\n      closing_match_pos = result.index(closing_regex, value_start)\n\n      break unless closing_match_pos","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/helpers/json_utils_helper.rb#L19-L55","documentation":"safe_parse_json_array attempts extraction, repair, and parse of an LLM/AI grader response expected to be a JSON array. If JSON.parse of the repaired string still raises JSON::ParserError inside the fallback path, it raises CedarAi::Errors::GraderError 'Invalid JSON response: could not extract valid JSON array'.","triggerScenarios":"The inner rescue branch receives model output whose extracted json_like block cannot be parsed even after escape_inner_quotes repair — e.g. trailing commas, unescaped control characters, or a non-array object.","commonSituations":"LLM returned prose mixed with code fences in an unexpected shape; model changed output format after a prompt or model version bump; response truncated by token limit mid-array.","solutions":["Tighten the prompt to require a bare JSON array with no surrounding text or code fences.","Validate/repair upstream (strip markdown fences, remove trailing commas) before calling safe_parse_json_array.","Retry the model call, optionally with temperature 0 or a stricter response_format.","Rescue CedarAi::Errors::GraderError in the grading pipeline and fall back to manual review."],"exampleFix":"// before\nresult = safe_parse_json_array(raw_model_output)\n// after\ntrimmed = raw_model_output[/\\[.*\\]/m]&.gsub(/,\\s*([\\]\\]])/, '\\1')\nresult = safe_parse_json_array(trimmed || raw_model_output)","handlingStrategy":"try-catch","validationCode":"candidate = raw[/\\[.*\\]/m]\nraise CedarAi::Errors::GraderError, 'no array found' unless candidate&.start_with?('[')","typeGuard":"def looks_like_json_array?(s)\n  s.is_a?(String) && s.strip.start_with?('[') && s.strip.end_with?(']')\nend","tryCatchPattern":"begin\n  grades = safe_parse_json_array(response)\nrescue CedarAi::Errors::GraderError => e\n  grades = retry_with_stricter_prompt(response)\nend","preventionTips":["Demand bare JSON arrays in prompts (no fences/prose)","Use structured output / response_format when the model API supports it","Strip code fences and trailing commas before parsing","Watch for truncation on long model outputs"],"tags":["json","parsing","ai-grader"],"backgroundTag":"invalid-json-response","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"}