instructure/canvas-lms · error · ImportError

Improper status for user #

Error message

Improper status for user #{user.user_id}

What it means

add_user only accepts statuses beginning with active, suspended, or deleted (case-insensitive). Any other string — typos, alternate vocabularies like 'inactive' or 'enabled' — is rejected with this ImportError, since Canvas SIS imports have a fixed status vocabulary.

Solutions

  1. Map source statuses to the exact Canvas vocabulary: active, suspended, deleted
  2. Strip/normalize the status string (downcase, trim) before building the Models::User
  3. Treat unrecognized statuses as 'active' only after confirming that matches your policy
  4. Log and skip rows with unmappable statuses

Example fix

// before
user = Sis::Models::User.new(user_id: id, login_id: login, status: hr_status) # e.g. 'inactive'
// after
mapped = { 'active' => 'active', 'enabled' => 'active', 'inactive' => 'suspended', 'terminated' => 'deleted' }.fetch(hr_status.downcase.strip, nil)
raise ArgumentError, "unmapped status #{hr_status}" unless mapped
user = Sis::Models::User.new(user_id: id, login_id: login, status: mapped)
Defensive patterns

Strategy: validation

Validate before calling

unless user.status.match?(/\A(active|suspended|deleted)/i)
  user.status = STATUS_MAP.fetch(user.status.to_s.downcase.strip, 'active')
end

Type guard

VALID_STATUSES = %w[active suspended deleted].freeze
def valid_status?(u)
  u.is_a?(Sis::Models::User) && VALID_STATUSES.any? { |s| u.status.to_s.downcase.start_with?(s) }
end

Try / catch

begin
  importer.add_user(user)
rescue SisImports::ImportError => e
  unmappable << user.user_id
end

Prevention

When it happens

Trigger: add_user with user.status like 'inactive', 'enabled', 'archived', or a localized/translated status word that does not match /\A(active|suspended|deleted)/i.

Common situations: Mapping another system's status vocabulary (e.g. HR 'inactive') directly into the SIS import; whitespace or translation issues in exports; someone setting 'deleted!' or 'deactivated' expecting it to work.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at lib/sis/user_importer.rb:78

        @batched_users = []
        @messages = messages
        @success_count = 0

        @roll_back_data = []
        @users_to_set_sis_batch_ids = []
        @pseudos_to_set_sis_batch_ids = []
        @users_to_add_account_associations = []
        @users_to_update_account_associations = []
        @users_to_sync = Set.new
        @authentication_providers = {}
      end

      # Pass a single instance of SIS::Models::User
      def add_user(user, login_only: false)
        raise ImportError, "No user_id given for a user" if user.user_id.blank?
        raise ImportError, "No login_id given for user #{user.user_id}" if user.login_id.blank?
        raise ImportError, "No status given for user #{user.user_id}" if user.status.blank?
        raise ImportError, "Improper status for user #{user.user_id}" unless user.status.match?(/\A(active|suspended|deleted)/i)
        return if @batch.skip_deletes? && user.status.match?(/deleted/i)

        if login_only && user.existing_user_id.blank? && user.existing_integration_id.blank? && user.existing_canvas_user_id.blank?
          raise ImportError, I18n.t("No existing user provided for login with SIS ID %{user_id}", user_id: user.user_id)
        end

        @batched_users << user
        process_batch(login_only:) if @batched_users.size >= BATCH_SIZE
      end

      def any_left_to_process?
        !@batched_users.empty?
      end

      def infer_user_name(user_row, prior_name = nil)
        if user_row.full_name.present?
          user_row.full_name
        elsif user_row.first_name.present? || user_row.last_name.present?

View on GitHub (pinned to 1c9f0bb801)