instructure/canvas-lms · error · RuntimeError
Error retrieving export state for Outcomes Service: #
Error message
Error retrieving export state for Outcomes Service: #{response.body} What it means
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.
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
Defensive patterns
Strategy: retry
Try / catch
begin
done = MigrationService.export_completed?(export_data)
rescue RuntimeError => e
raise unless e.message.start_with?('Error retrieving export state')
attempts += 1
retry if attempts < 3 && e.message.include?('50')
Canvas::Errors.capture(e)
end Prevention
- Handle 401 by refreshing JWT before re-polling
- Cap poll duration to stay within token lifetime
- Log response bodies to distinguish auth vs outage
When it happens
Trigger: GET to /api/content_exports/:id returns 4xx/5xx — expired/invalid JWT in headers_for, unknown export_id, or the service being down/erroring.
Common situations: Token expiry during long polls; export id stale or deleted; outcomes service outage or wrong URL configuration.
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 queueing export for Outcomes Service: #
- Error retrieving export for Outcomes Service: #
- # : #
- Error parsing JSON results from Outcomes Service: #
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/02812208c8a6c21b.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/outcomes_service/migration_service.rb:72
def export_completed?(export_data)
content_export_url = "#{OutcomesService::Service.url(export_data[:course])}/api/content_exports/#{export_data[:export_id]}"
response = CanvasHttp.get(
content_export_url,
headers_for(export_data[:course], "content_migration.export", id: export_data[:export_id])
)
if /^2/.match?(response.code.to_s)
json = JSON.parse(response.body)
case json["state"]
when "completed"
true
when "failed"
raise "Content Export for Outcomes Service failed"
else
false
end
else
raise "Error retrieving export state for Outcomes Service: #{response.body}"
end
end
def retrieve_export(export_data)
content_export_url = "#{OutcomesService::Service.url(export_data[:course])}/api/content_exports/#{export_data[:export_id]}"
response = CanvasHttp.get(
content_export_url,
headers_for(export_data[:course], "content_migration.export", id: export_data[:export_id])
)
if /^2/.match?(response.code.to_s)
json = JSON.parse(response.body)
json["data"]
else
raise "Error retrieving export for Outcomes Service: #{response.body}"
end
end
def send_imported_content(course, content_migration, imported_content)View on GitHub (pinned to 1c9f0bb801)