instructure/canvas-lms · error · ImportError

Cannot delete the sub_account with ID: #

Error message

Cannot delete the sub_account with ID: #{account_id} because it has active sub accounts.

What it means

When an account import requests status 'deleted', the importer checks account.sub_accounts.active.exists? and refuses to delete an account that still has active child accounts, raising ImportError. Canvas requires bottom-up deletion of the account hierarchy.

Solutions

  1. Include 'deleted' rows for all active child accounts in the same SIS batch, ordered before the parent.
  2. Manually delete or deactivate the child accounts in Canvas first.
  3. Re-check the account tree in the Canvas UI for remaining active sub-accounts.

Example fix

// before
# accounts.csv: delete parent while child stays active
add_account('parent1', nil, 'deleted', 'Parent', nil)
// after
add_account('child1', 'parent1', 'deleted', 'Child', nil)
add_account('parent1', nil, 'deleted', 'Parent', nil)
Defensive patterns

Strategy: try-catch

Validate before calling

if status.to_s =~ /deleted/i
  acct = root_account.all_accounts.find_by(sis_source_id: account_id)
  raise "delete children first" if acct&.sub_accounts&.active&.exists?
end

Try / catch

begin
  importer.add_account(id, parent_id, 'deleted', name, integration_id)
rescue SIS::BaseImporter::ImportError => e
  logger.warn("deferring delete: #{e.message}")
  retry_later
end

Prevention

When it happens

Trigger: add_account with status 'deleted' for an account that has at least one active (non-deleted workflow_state) sub-account in the database.

Common situations: Deleting a whole branch of the account tree from SIS without ordering leaves first; partial imports where children were not deleted in the same or prior run.

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/b88a9413ccbc9a7e. Report an issue: GitHub.

Appendix: source

Thrown at lib/sis/account_importer.rb:90

        account ||= @root_account.sub_accounts.new

        account.root_account = @root_account
        if account.new_record? || !account.stuck_sis_fields.include?(:parent_account_id) || Account.sis_stickiness_options[:add_sis_stickiness]
          account.parent_account = parent || @root_account
        end

        # only update the name on new records, and ones that haven't been changed since the last sis import
        account.name = name if name.present? && (account.new_record? || !account.stuck_sis_fields.include?(:name))

        account.integration_id = integration_id if integration_id.present?
        account.sis_source_id = account_id

        if status.present?
          case status
          when /active/i
            account.workflow_state = "active"
          when /deleted/i
            raise ImportError, "Cannot delete the sub_account with ID: #{account_id} because it has active sub accounts." if account.sub_accounts.active.exists?
            raise ImportError, "Cannot delete the sub_account with ID: #{account_id} because it has active courses." if account.courses.active.exists?

            account.workflow_state = "deleted"
          end
        end

        @accounts_cache[account.sis_source_id] = account

        unless account.changed?
          @success_count += 1
          accounts_to_set_sis_batch_ids << account.id unless account.sis_batch_id == @batch.try(:id)
          return
        end

        account.sis_batch_id = @batch.id

        update_account_associations = account.root_account_id_changed? || account.parent_account_id_changed?
        if account.save

View on GitHub (pinned to 1c9f0bb801)