instructure/canvas-lms · error · ImportError

Improper status "# " for # # .

Error message

Improper status "#{status}" for #{type_display_name(type)} #{id}.

What it means

Raised when the 'status' of a group or differentiation tag row is not one of the accepted SIS statuses: available, closed, completed, or deleted (case-insensitive). The importer enforces this vocabulary so downstream record state transitions are well-defined. The message echoes the invalid status plus the item type and id.

Solutions

  1. Change the status to one of: available, closed, completed, deleted.
  2. Map source-system statuses to SIS statuses in your CSV generator.
  3. Use 'deleted' to deactivate the group.
  4. Validate the status column against the allowed pattern before upload.

Example fix

# before
group_id,course_id,name,status
G101,C200,Study Group A,active
# after
group_id,course_id,name,status
G101,C200,Study Group A,available
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = %w[available closed completed deleted]
unless ALLOWED.any? { |s| status.to_s.downcase.start_with?(s) }
  raise ArgumentError, "status must be one of #{ALLOWED.join(', ')}"
end

Try / catch

begin
  importer.add_group(gid, cat_id, acct_id, course_id, name, status)
rescue ImportError => e
  logger.warn("Bad status row: #{e.message}")
end

Prevention

When it happens

Trigger: add_group/add_tag receiving a status like 'active', '', 'unpublished', or a typo such as 'availible' — anything not matching /\A(available|closed|completed|deleted)/i.

Common situations: Migrating CSVs from another LMS with different status names; typo'd statuses; translated status columns; legacy scripts emitting 'active' instead of 'available'.

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

Appendix: source

Thrown at lib/sis/group_importer.rb:55

        @batch = batch
        @root_account = root_account
        @logger = logger
        @success_count = 0
        @roll_back_data = []
        @accounts_cache = {}
        @courses_cache = {}
      end

      private

      def type_display_name(type)
        type.tr("_", " ")
      end

      def invalid_import_item?(id, name, status, type)
        raise ImportError, "No #{(type == "group") ? type : "tag"}_id given for a #{type_display_name(type)}." unless id
        raise ImportError, "No name given for #{type_display_name(type)} #{id}." if name.blank?
        raise ImportError, "Improper status \"#{status}\" for #{type_display_name(type)} #{id}." unless /\A(available|closed|completed|deleted)/i.match?(status)
        return true if @batch.skip_deletes? && status =~ /deleted/i

        false
      end

      def find_context(id, id_type, type, item_id)
        context = nil
        cache = (id_type == :course_id) ? @courses_cache : @accounts_cache

        if id
          context = cache[id]
          context ||= if id_type == :course_id
                        @root_account.all_courses.active.find_by(sis_source_id: id)
                      else
                        @root_account.all_accounts.active.find_by(sis_source_id: id)
                      end

          unless context

View on GitHub (pinned to 1c9f0bb801)