instructure/canvas-lms · error · OSFetchError
Error retrieving results from Outcomes Service: #
Error message
Error retrieving results from Outcomes Service: #{response.body} What it means
get_request_page checks that the Outcomes Service responded with a 2xx status code (/^2/ match on response.code). Any non-2xx (4xx auth failure, 404, 5xx server error) is raised as OSFetchError containing the response body, since authoritative outcome results could not be retrieved.
Solutions
- Read the response body in the error — it usually contains the service's error reason
- Verify JWT generation/signing and that shared secrets match between Canvas and the outcomes service
- Confirm the outcomes service URL configuration for the environment
- Check outcomes service logs for the corresponding 4xx/5xx and fix server-side or correct the request params
Example fix
// before
else
raise OSFetchError, "Error retrieving results from Outcomes Service: #{response.body}"
// after
else
Canvas::Errors.capture(OSFetchError.new("status=#{response.code} body=#{response.body[0, 500]}"))
raise OSFetchError, "Error retrieving results from Outcomes Service (status #{response.code}): #{response.body}" Defensive patterns
Strategy: try-catch
Validate before calling
raise OSFetchError, 'non-2xx' unless response.code.to_s.start_with?('2') Try / catch
begin
page = get_request_page(context, params)
rescue OSFetchError => e
Canvas::Errors.capture(e)
render json: { error: 'outcomes service unavailable' }, status: :bad_gateway
end Prevention
- Verify JWT signing/secrets on every deploy
- Environment-check the outcomes service URL
- Handle 401/403 distinctly from 5xx in callers
When it happens
Trigger: Outcomes Service returns 401/403 (bad or expired JWT), 404 (wrong URL or context id), or 500 (server-side error) during a get_request/get_request_page call.
Common situations: Expired or mis-signed services JWT; wrong outcomes service URL per-environment; the context (course/account) not existing in the outcomes service; outcomes service internal error or maintenance.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Content Export for Outcomes Service failed
- # : #
- Error parsing JSON results from Outcomes Service: #
- Error queueing export for Outcomes Service: #
- Error retrieving export for Outcomes Service: #
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/5245517768e3e65c.
Report an issue: GitHub.
Appendix: source
Thrown at app/helpers/canvas_outcomes_helper.rb:135
results.each do |result|
next if result[:attempts].nil?
result[:attempts].each do |attempt|
# Initially metadata was a string, now it's a jsonb data type. When it was a string, canvas needed
# to parse the result returned from outcome service
next unless attempt[:metadata].is_a? String
attempt[:metadata] = JSON.parse(attempt[:metadata]) unless attempt[:metadata].nil?
attempt[:metadata] = attempt[:metadata].deep_symbolize_keys unless attempt[:metadata].nil?
end
end
end
{ results:, total_pages: }
rescue
raise OSFetchError, "Error parsing JSON results from Outcomes Service: #{response.body}"
end
else
raise OSFetchError, "Error retrieving results from Outcomes Service: #{response.body}"
end
end
def outcome_has_alignments?(outcome, context)
response = get_outcome_alignments(context, outcome.id, { includes: "alignments" })
return false if response.nil?
response.first[:alignments].count > 0
end
def outcome_has_authoritative_results?(outcome, context)
assignments = Assignment.active.where(context:).quiz_lti
return false if assignments.blank?
assignment_ids = assignments.pluck(:id).join(",")
assignment_type = "canvas.assignment.quizzes"
params = {View on GitHub (pinned to 1c9f0bb801)