{"record":{"id":"7f93dbda41cbaa7c","repo":"instructure/canvas-lms","slug":"failed-to-fetch-results-for-context-context-id-params","errorCode":null,"errorMessage":"Failed to fetch results for context #{context.id} #{params}","messagePattern":"Failed to fetch results for context #(.+?) #(.+?)","errorType":"exception","errorClass":"OSFetchError","httpStatus":null,"severity":"error","filePath":"app/helpers/canvas_outcomes_helper.rb","lineNumber":105,"sourceCode":"  def get_request_page(context, domain, endpoint, jwt, params, page_num, per_page = DEFAULT_PER_PAGE, response_parser_callback = nil)\n    retry_count = 0\n    pagination_params = {\n      per_page:,\n      page: page_num\n    }\n    params = params.merge(pagination_params)\n\n    begin\n      response = CanvasHttp.get(\n        build_request_url(protocol, domain, endpoint, params),\n        {\n          \"Authorization\" => jwt\n        }\n      )\n    rescue\n      retry_count += 1\n      retry if retry_count < MAX_RETRIES\n      raise OSFetchError, \"Failed to fetch results for context #{context.id} #{params}\"\n    end\n\n    if /^2/.match?(response.code.to_s)\n      per_page = response.header[\"Per-Page\"].to_i\n      total_pages = (response.header[\"Total\"].to_f / per_page).ceil\n      begin\n        # If a response_parser_callback is provided, use it to parse the response\n        if response_parser_callback\n          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","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/helpers/canvas_outcomes_helper.rb#L87-L123","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check Outcomes Service availability/health from the Canvas host (curl the configured URL)","Verify the outcomes service URL and network egress config for the environment","Inspect logs for the underlying exception raised on each retry to distinguish timeout vs connection-refused vs DNS","If transient, re-run the request; consider increasing MAX_RETRIES or adding backoff"],"exampleFix":"// before\n# all retries fail, no visibility into why\nrescue\n  retry_count += 1\n  retry if retry_count < MAX_RETRIES\n  raise OSFetchError, \"Failed to fetch results for context #{context.id} #{params}\"\n// after\nrescue => e\n  retry_count += 1\n  retry if retry_count < MAX_RETRIES\n  Canvas::Errors.capture(e)\n  raise OSFetchError, \"Failed to fetch results for context #{context.id} #{params}: #{e.class} #{e.message}\"","handlingStrategy":"retry","validationCode":"# pre-check reachability\nuri = URI(outcomes_service_url)\nNet::HTTP.start(uri.host, uri.port, open_timeout: 2) { |h| h.head('/') }","typeGuard":null,"tryCatchPattern":"begin\n  results = get_request(context, params)\nrescue OSFetchError => e\n  Canvas::Errors.capture(e)\n  fallback_results(context)\nend","preventionTips":["Monitor outcomes service health/endpoints","Set sensible MAX_RETRIES with exponential backoff","Alert on OSFetchError rates per environment"],"tags":["http","network","outcomes-service","retry-exhausted"],"backgroundTag":"http-request-failed","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"}