instructure/canvas-lms · error · OSFetchError

Failed to fetch results for context #

Error message

Failed to fetch results for context #{context.id} #{params}

What it means

get_request_page in CanvasOutcomesHelper makes an HTTP request to the external Outcomes Service and retries up to MAX_RETRIES. If every attempt raises (network error, DNS failure, TLS error, etc.), it wraps the failure in OSFetchError with the context id and params so the caller knows the authoritative outcome results could not be fetched.

Solutions

  1. Check Outcomes Service availability/health from the Canvas host (curl the configured URL)
  2. Verify the outcomes service URL and network egress config for the environment
  3. Inspect logs for the underlying exception raised on each retry to distinguish timeout vs connection-refused vs DNS
  4. If transient, re-run the request; consider increasing MAX_RETRIES or adding backoff

Example fix

// before
# all retries fail, no visibility into why
rescue
  retry_count += 1
  retry if retry_count < MAX_RETRIES
  raise OSFetchError, "Failed to fetch results for context #{context.id} #{params}"
// after
rescue => e
  retry_count += 1
  retry if retry_count < MAX_RETRIES
  Canvas::Errors.capture(e)
  raise OSFetchError, "Failed to fetch results for context #{context.id} #{params}: #{e.class} #{e.message}"
Defensive patterns

Strategy: retry

Validate before calling

# pre-check reachability
uri = URI(outcomes_service_url)
Net::HTTP.start(uri.host, uri.port, open_timeout: 2) { |h| h.head('/') }

Try / catch

begin
  results = get_request(context, params)
rescue OSFetchError => e
  Canvas::Errors.capture(e)
  fallback_results(context)
end

Prevention

When it happens

Trigger: The Outcomes Service host is unreachable or times out on all MAX_RETRIES attempts during get_request/get_request_page; connection refused or reset between Canvas and the outcomes service.

Common situations: Outcomes Service outage or deployment; wrong service URL in config; firewall/security-group blocking egress from the Canvas app container; DNS misconfiguration in an environment.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/7f93dbda41cbaa7c. Report an issue: GitHub.

Appendix: source

Thrown at app/helpers/canvas_outcomes_helper.rb:105

  def get_request_page(context, domain, endpoint, jwt, params, page_num, per_page = DEFAULT_PER_PAGE, response_parser_callback = nil)
    retry_count = 0
    pagination_params = {
      per_page:,
      page: page_num
    }
    params = params.merge(pagination_params)

    begin
      response = CanvasHttp.get(
        build_request_url(protocol, domain, endpoint, params),
        {
          "Authorization" => jwt
        }
      )
    rescue
      retry_count += 1
      retry if retry_count < MAX_RETRIES
      raise OSFetchError, "Failed to fetch results for context #{context.id} #{params}"
    end

    if /^2/.match?(response.code.to_s)
      per_page = response.header["Per-Page"].to_i
      total_pages = (response.header["Total"].to_f / per_page).ceil
      begin
        # If a response_parser_callback is provided, use it to parse the response
        if response_parser_callback
          results = response_parser_callback.call(response)
        else
          results = JSON.parse(response.body).deep_symbolize_keys[:results]
          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

View on GitHub (pinned to 1c9f0bb801)