instructure/canvas-lms · error · ImportError

User # didn't exist for # user

Error message

User #{user_id} didn't exist for #{is_tags ? "differentiation tag" : "group"} user

What it means

After resolving the pseudonym by sis_user_id, the importer raises if no user was found — i.e. the referenced user does not exist on this root account (or the pseudonym lookup failed), so the membership cannot be created.

Solutions

  1. Ensure the user exists with that sis_user_id on the target root account (import users.csv first)
  2. Check the sis_user_id spelling/case and that you are not using a Canvas numeric id where a SIS id is expected
  3. Verify you are importing into the correct root account/shard
  4. Create or re-activate the user's pseudonym before running the group membership import

Example fix

# before (user not yet imported)
groups_memberships.csv references u_500, users.csv absent
# after
import users.csv creating pseudonym sis_user_id u_500, then run memberships
Defensive patterns

Strategy: validation

Validate before calling

user_id_ok = root_account.pseudonyms.active.by_sis_user_id(user_id).exists?
raise "user #{user_id} not on this root account" unless user_id_ok

Try / catch

begin
  importer.add_group_membership(uid, gid, status)
rescue SIS::GroupImporter::ImportError => e
  raise unless e.message =~ /didn't exist for .*user/
  Rails.logger.warn("Unknown user #{uid}; skipping row")
end

Prevention

When it happens

Trigger: add_group_membership with a user_id whose pseudonym (sis_user_id) does not exist under @root_account; user was deleted; import run on the wrong root account/shard; users.csv not imported before group memberships.

Common situations: Cross-shard or multi-root-account SIS setups where the id lives on another account; typo in the SIS user id; users file processed after groups file out of order; user enrolled in a different root account.

Understand the failure class

Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.

Related errors


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

Appendix: source

Thrown at lib/sis/group_membership_importer.rb:59

      end

      def add_group_membership(user_id, group_id, status, is_tags: false)
        user_id = user_id.to_s
        group_id = group_id.to_s
        raise ImportError, "No #{is_tags ? "tag_id" : "group_id"} given for a #{is_tags ? "differentiation tag" : "group"} user" if group_id.blank?
        raise ImportError, "No user_id given for a #{is_tags ? "differentiation tag" : "group"} user" if user_id.blank?
        raise ImportError, "Improper status \"#{status}\" for a #{is_tags ? "differentiation tag" : "group"} user" unless /\A(accepted|deleted)/i.match?(status)
        return if @batch.skip_deletes? && status =~ /deleted/i

        pseudo = @root_account.pseudonyms.find_by(sis_user_id: user_id)
        user = pseudo&.user

        group = @groups_cache[group_id]
        scope = is_tags ? @root_account.all_differentiation_tags : @root_account.all_groups
        group ||= scope.where(sis_source_id: group_id).preload(:context).take
        @groups_cache[group.sis_source_id] = group if group

        raise ImportError, "User #{user_id} didn't exist for #{is_tags ? "differentiation tag" : "group"} user" unless user
        raise ImportError, "#{is_tags ? "Differentiation tag" : "Group"} #{group_id} didn't exist for #{is_tags ? "differentiation tag" : "group"} user" unless group

        if group.context.is_a?(Course) && !group.context.all_real_users.where(id: user.id).exists?
          raise ImportError, "User #{user_id} doesn't have an enrollment in the course of #{is_tags ? "differentiation tag" : "group"} #{group_id}."
        end

        if group && is_tags
          raise ImportError, "Differentiation Tags are not enabled for Account #{group.context.account.id}." unless group.context.account.allow_assign_to_differentiation_tags?
        end

        # can't query group.group_memberships, since that excludes deleted memberships
        group_membership = GroupMembership.where(group_id: group, user_id: user)
                                          .order(Arel.sql("CASE WHEN workflow_state = 'accepted' THEN 0 ELSE 1 END")).take
        group_membership ||= group.group_memberships.build(user:)

        group_membership.sis_batch_id = @batch.id

        case status

View on GitHub (pinned to 1c9f0bb801)