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 courses. What it means
Similar to the sub-accounts guard: deleting an account via SIS is blocked with ImportError when account.courses.active.exists? — the account still contains active courses. Courses must be deleted/concluded before their account can be deleted.
Solutions
- Delete or conclude all active courses in the account (via SIS courses.csv rows with status 'deleted' or the UI) before deleting the account.
- Move active courses to a different account, then delete.
- Verify with a console check: account.courses.active.exists?
Example fix
// before
# course still active
add_account('dept1', 'root', 'deleted', 'Department', nil)
// after
# courses.csv: mark course deleted first, then
add_account('dept1', 'root', 'deleted', 'Department', nil) Defensive patterns
Strategy: try-catch
Validate before calling
acct = root_account.all_accounts.find_by(sis_source_id: account_id) raise "delete/complete courses first" if acct&.courses&.active&.exists?
Try / catch
begin
importer.add_account(id, parent_id, 'deleted', name, integration_id)
rescue SIS::BaseImporter::ImportError => e
logger.warn("blocked: #{e.message}")
end Prevention
- Import course deletions for the subtree before account deletion
- Report blocked deletions to SIS admins instead of failing the whole batch
- Schedule account retirement after course clean-up
When it happens
Trigger: add_account with status 'deleted' for an account that has at least one active course under it.
Common situations: Retiring an academic department account whose courses are still unpublished/active; SIS sync not including course deletions for that subtree.
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
- Cannot delete the sub_account with ID: #
- A cross-listing referenced a non-existent section #
- A deleted cross-listing failed: #
- A new_id, '# ', referenced an existing # and the # with #…
- A new_integration_id, '#
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/f4e814507e0bd9b8.
Report an issue: GitHub.
Appendix: source
Thrown at lib/sis/account_importer.rb:91
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
data = SisBatchRollBackData.build_data(sis_batch: @batch, context: account)View on GitHub (pinned to 1c9f0bb801)