instructure/canvas-lms · warning · ActiveRecord::RecordNotFound

Import is still being processed

Error message

Import is still being processed

What it means

In the outcome import created_group_ids endpoint, Canvas raises ActiveRecord::RecordNotFound with 'Import is still being processed' when the import has neither failed nor succeeded (workflow_state is still processing/created). The rescue maps it to a 404 JSON response; the group IDs are not yet available.

Solutions

  1. Poll GET .../outcome_imports/:id until workflow_state == 'succeeded', then call created_group_ids
  2. Add a delay/backoff between import creation and the first results fetch
  3. Handle the 404 with this message by retrying after an interval rather than treating it as a permanent failure
  4. For 'latest', ensure no other import was started concurrently that is still processing

Example fix

# before
ids = api.get("/api/v1/accounts/1/outcome_imports/latest/created_group_ids")
# after
loop do
  imp = api.get("/api/v1/accounts/1/outcome_imports/latest")
  break if imp['workflow_state'] == 'succeeded'
  sleep 5
end
ids = api.get("/api/v1/accounts/1/outcome_imports/latest/created_group_ids")
Defensive patterns

Strategy: retry

Validate before calling

const state = (await api.get(`.../outcome_imports/${id}`)).workflow_state;
if (state !== 'succeeded') return null; // not ready yet, caller should wait

Try / catch

async function fetchGroupIdsWithRetry(id, { tries = 12, delayMs = 10000 } = {}) {
  for (let i = 0; i < tries; i++) {
    try { return await fetchGroupIds(id); }
    catch (e) { const msg = e.response?.data?.message; if (e.response?.status === 404 && msg === 'Import is still being processed') { await sleep(delayMs); continue; } throw e; }
  }
  throw new Error('import still processing after retries');
}

Prevention

When it happens

Trigger: GET .../outcome_imports/:id/created_group_ids (or :id = 'latest') immediately after POSTing a large outcome import, before the background importer has finished.

Common situations: Scripts that create an import and immediately fetch results without polling, large CSVs in queues under load, or checking 'latest' while a newer import is still running.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at app/controllers/outcome_imports_api_controller.rb:234

  #
  #   Examples:
  #     curl 'https://<canvas>/api/v1/accounts/<account_id>/outcome_imports/outcomes_group_ids/<outcome_import_id>' \
  #         -H "Authorization: Bearer <token>"
  #     curl 'https://<canvas>/api/v1/courses/<course_id>/outcome_imports/outcome_group_ids/<outcome_import_id>' \
  #         -H "Authorization: Bearer <token>"
  #
  # @returns array of outcome ids
  def created_group_ids
    if authorized_action(@context, @current_user, %i[import_outcomes manage_outcomes])
      begin
        import = if params[:id] == "latest"
                   @context.latest_outcome_import or raise ActiveRecord::RecordNotFound
                 else
                   @context.outcome_imports.find(params[:id])
                 end

        raise ActiveRecord::RecordNotFound, "Import has failed" if import.failed?
        raise ActiveRecord::RecordNotFound, "Import is still being processed" unless import.succeeded?

        render json: LearningOutcomeGroup.where(outcome_import_id: import.id).pluck(:id).map(&:to_s)
      rescue ActiveRecord::RecordNotFound => e
        render json: { message: e.message }, status: :not_found
      end
    end
  end

  private

  def body_file
    file_obj = request.body

    # rubocop:disable Style/TrivialAccessors -- not a Class
    file_obj.instance_exec do
      def set_file_attributes(filename, content_type)
        @original_filename = filename
        @content_type = content_type

View on GitHub (pinned to 1c9f0bb801)