{"record":{"id":"d2021b6685764c06","repo":"instructure/canvas-lms","slug":"import-is-still-being-processed","errorCode":null,"errorMessage":"Import is still being processed","messagePattern":"Import is still being processed","errorType":"exception","errorClass":"ActiveRecord::RecordNotFound","httpStatus":404,"severity":"warning","filePath":"app/controllers/outcome_imports_api_controller.rb","lineNumber":234,"sourceCode":"  #\n  #   Examples:\n  #     curl 'https://<canvas>/api/v1/accounts/<account_id>/outcome_imports/outcomes_group_ids/<outcome_import_id>' \\\n  #         -H \"Authorization: Bearer <token>\"\n  #     curl 'https://<canvas>/api/v1/courses/<course_id>/outcome_imports/outcome_group_ids/<outcome_import_id>' \\\n  #         -H \"Authorization: Bearer <token>\"\n  #\n  # @returns array of outcome ids\n  def created_group_ids\n    if authorized_action(@context, @current_user, %i[import_outcomes manage_outcomes])\n      begin\n        import = if params[:id] == \"latest\"\n                   @context.latest_outcome_import or raise ActiveRecord::RecordNotFound\n                 else\n                   @context.outcome_imports.find(params[:id])\n                 end\n\n        raise ActiveRecord::RecordNotFound, \"Import has failed\" if import.failed?\n        raise ActiveRecord::RecordNotFound, \"Import is still being processed\" unless import.succeeded?\n\n        render json: LearningOutcomeGroup.where(outcome_import_id: import.id).pluck(:id).map(&:to_s)\n      rescue ActiveRecord::RecordNotFound => e\n        render json: { message: e.message }, status: :not_found\n      end\n    end\n  end\n\n  private\n\n  def body_file\n    file_obj = request.body\n\n    # rubocop:disable Style/TrivialAccessors -- not a Class\n    file_obj.instance_exec do\n      def set_file_attributes(filename, content_type)\n        @original_filename = filename\n        @content_type = content_type","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/controllers/outcome_imports_api_controller.rb#L216-L252","documentation":"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.","triggerScenarios":"GET .../outcome_imports/:id/created_group_ids (or :id = 'latest') immediately after POSTing a large outcome import, before the background importer has finished.","commonSituations":"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.","solutions":["Poll GET .../outcome_imports/:id until workflow_state == 'succeeded', then call created_group_ids","Add a delay/backoff between import creation and the first results fetch","Handle the 404 with this message by retrying after an interval rather than treating it as a permanent failure","For 'latest', ensure no other import was started concurrently that is still processing"],"exampleFix":"# before\nids = api.get(\"/api/v1/accounts/1/outcome_imports/latest/created_group_ids\")\n# after\nloop do\n  imp = api.get(\"/api/v1/accounts/1/outcome_imports/latest\")\n  break if imp['workflow_state'] == 'succeeded'\n  sleep 5\nend\nids = api.get(\"/api/v1/accounts/1/outcome_imports/latest/created_group_ids\")","handlingStrategy":"retry","validationCode":"const state = (await api.get(`.../outcome_imports/${id}`)).workflow_state;\nif (state !== 'succeeded') return null; // not ready yet, caller should wait","typeGuard":null,"tryCatchPattern":"async function fetchGroupIdsWithRetry(id, { tries = 12, delayMs = 10000 } = {}) {\n  for (let i = 0; i < tries; i++) {\n    try { return await fetchGroupIds(id); }\n    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; }\n  }\n  throw new Error('import still processing after retries');\n}","preventionTips":["Poll the import status endpoint with backoff until succeeded","Never assume an import completes synchronously after POST","Set a max retry budget and alert if exceeded","Account for queue latency on large CSVs"],"tags":["outcome-import","async-processing","polling","canvas-lms"],"backgroundTag":"invalid-state-transition","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"}