{"record":{"id":"8beaad9c271dea0a","repo":"instructure/canvas-lms","slug":"error-parsing-json-results-from-outcomes-service-response","errorCode":null,"errorMessage":"Error parsing JSON results from Outcomes Service: #{response.body}","messagePattern":"Error parsing JSON results from Outcomes Service: #(.+?)","errorType":"exception","errorClass":"OSFetchError","httpStatus":null,"severity":"error","filePath":"app/helpers/canvas_outcomes_helper.rb","lineNumber":132,"sourceCode":"          results = response_parser_callback.call(response)\n        else\n          results = JSON.parse(response.body).deep_symbolize_keys[:results]\n          results.each do |result|\n            next if result[:attempts].nil?\n\n            result[:attempts].each do |attempt|\n              # Initially metadata was a string, now it's a jsonb data type. When it was a string, canvas needed\n              # to parse the result returned from outcome service\n              next unless attempt[:metadata].is_a? String\n\n              attempt[:metadata] = JSON.parse(attempt[:metadata]) unless attempt[:metadata].nil?\n              attempt[:metadata] = attempt[:metadata].deep_symbolize_keys unless attempt[:metadata].nil?\n            end\n          end\n        end\n        { results:, total_pages: }\n      rescue\n        raise OSFetchError, \"Error parsing JSON results from Outcomes Service: #{response.body}\"\n      end\n    else\n      raise OSFetchError, \"Error retrieving results from Outcomes Service: #{response.body}\"\n    end\n  end\n\n  def outcome_has_alignments?(outcome, context)\n    response = get_outcome_alignments(context, outcome.id, { includes: \"alignments\" })\n    return false if response.nil?\n\n    response.first[:alignments].count > 0\n  end\n\n  def outcome_has_authoritative_results?(outcome, context)\n    assignments = Assignment.active.where(context:).quiz_lti\n\n    return false if assignments.blank?\n","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/helpers/canvas_outcomes_helper.rb#L114-L150","documentation":"After a 2xx response from the Outcomes Service, get_request_page parses the body as JSON and normalizes result attempts. If JSON.parse (and the subsequent shape handling) raises, it is rescued and re-raised as OSFetchError including the raw response body, because a 200 with unparseable/missing content still yields no usable results.","triggerScenarios":"Outcomes Service returns a 2xx whose body is not valid JSON (HTML error page from a proxy, empty body, truncated response) or valid JSON with an unexpected shape (missing 'results'/'attempts' keys where deep_symbolize_keys/normalization is applied).","commonSituations":"Load balancer or auth proxy intercepting and returning an HTML 200 page; outcomes service bug emitting malformed JSON; API version drift changing the response envelope.","solutions":["Log/inspect the response body included in the error to see what actually came back","Verify the Outcomes Service version matches the response schema Canvas expects","Check for proxies (LB, auth middleware) returning HTML with 200 status","Add a schema/validation step before parsing to fail fast with a clearer message"],"exampleFix":"// before\nrescue\n  raise OSFetchError, \"Error parsing JSON results from Outcomes Service: #{response.body}\"\n// after\nrescue JSON::ParserError => e\n  raise OSFetchError, \"Error parsing JSON results from Outcomes Service (#{e.message}): #{response.body[0, 500]}\"","handlingStrategy":"try-catch","validationCode":"# validate body looks like JSON before parsing\nraise OSFetchError, 'empty body' if response.body.to_s.strip.empty?\nJSON.parse(response.body) # raises JSON::ParserError with position info","typeGuard":null,"tryCatchPattern":"begin\n  parsed = JSON.parse(response.body)\nrescue JSON::ParserError => e\n  raise OSFetchError, \"Error parsing JSON results from Outcomes Service: #{e.message} #{response.body[0, 200]}\"\nend","preventionTips":["Log a truncated body on parse failure","Pin and test against the outcomes service API version","Detect proxy/HTML 200 responses before parsing"],"tags":["http","json","outcomes-service","parse-error"],"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"}