{"record":{"id":"02812208c8a6c21b","repo":"instructure/canvas-lms","slug":"error-retrieving-export-state-for-outcomes-service-response","errorCode":null,"errorMessage":"Error retrieving export state for Outcomes Service: #{response.body}","messagePattern":"Error retrieving export state for Outcomes Service: #(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"app/models/outcomes_service/migration_service.rb","lineNumber":72,"sourceCode":"\n      def export_completed?(export_data)\n        content_export_url = \"#{OutcomesService::Service.url(export_data[:course])}/api/content_exports/#{export_data[:export_id]}\"\n        response = CanvasHttp.get(\n          content_export_url,\n          headers_for(export_data[:course], \"content_migration.export\", id: export_data[:export_id])\n        )\n        if /^2/.match?(response.code.to_s)\n          json = JSON.parse(response.body)\n          case json[\"state\"]\n          when \"completed\"\n            true\n          when \"failed\"\n            raise \"Content Export for Outcomes Service failed\"\n          else\n            false\n          end\n        else\n          raise \"Error retrieving export state for Outcomes Service: #{response.body}\"\n        end\n      end\n\n      def retrieve_export(export_data)\n        content_export_url = \"#{OutcomesService::Service.url(export_data[:course])}/api/content_exports/#{export_data[:export_id]}\"\n        response = CanvasHttp.get(\n          content_export_url,\n          headers_for(export_data[:course], \"content_migration.export\", id: export_data[:export_id])\n        )\n        if /^2/.match?(response.code.to_s)\n          json = JSON.parse(response.body)\n          json[\"data\"]\n        else\n          raise \"Error retrieving export for Outcomes Service: #{response.body}\"\n        end\n      end\n\n      def send_imported_content(course, content_migration, imported_content)","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/outcomes_service/migration_service.rb#L54-L90","documentation":"export_completed? raises this when the outcomes service responds with a non-2xx status while polling export state, embedding the response body. It means the state poll itself failed, not that the export failed.","triggerScenarios":"GET to /api/content_exports/:id returns 4xx/5xx — expired/invalid JWT in headers_for, unknown export_id, or the service being down/erroring.","commonSituations":"Token expiry during long polls; export id stale or deleted; outcomes service outage or wrong URL configuration.","solutions":["Inspect response body in the message (401/404/500 etc.) and fix auth or URL accordingly","Verify the export_id and course are correct for the configured outcomes service","Check/refresh headers_for JWT generation","Retry the poll after service recovery"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  done = MigrationService.export_completed?(export_data)\nrescue RuntimeError => e\n  raise unless e.message.start_with?('Error retrieving export state')\n  attempts += 1\n  retry if attempts < 3 && e.message.include?('50')\n  Canvas::Errors.capture(e)\nend","preventionTips":["Handle 401 by refreshing JWT before re-polling","Cap poll duration to stay within token lifetime","Log response bodies to distinguish auth vs outage"],"tags":["ruby","http","outcomes-service","polling"],"backgroundTag":"http-error-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"}