elsa-workflows/elsa-core · error · InvalidOperationException

The Elsa user was deleted while its external identity link…

Error message

The Elsa user was deleted while its external identity link was being created.

What it means

Thrown by EFCoreExternalIdentityProvisioner.EnsureLinkedUserStillExistsAsync, called at the end of CreateLinkOrGetExistingAsync. After creating the external identity link, the provisioner re-checks (via IUserProvisioningService.ExistsAsync) that the just-in-time Elsa user still exists; if a concurrent actor deleted the user, the freshly created link is deleted and this error is thrown so no dangling link or credentials are issued.

Solutions

  1. Retry the external sign-in flow; the next attempt will recreate the just-in-time user and link from scratch.
  2. Ensure IUserProvisioningService.RemoveAsync callers also remove dependent identity links atomically (same transaction or cascade) to avoid the race.
  3. Serialize user-deletion operations against sign-in provisioning (e.g. unique constraint on link user id plus retry on conflict) if concurrent deletes are frequent.
  4. Check logs for who deleted the user (cleanup jobs, admin actions) and adjust job schedules to avoid first-login windows.

Example fix

// before: blind delete then throw (current compensation is automatic; caller should retry)
catch (InvalidOperationException ex) when (ex.Message.Contains("deleted while its external identity link was being created"))
{
    // after: retry provisioning once
    return await provisioner.CreateLinkOrGetExistingAsync(request, cancellationToken);
}
Defensive patterns

Strategy: retry

Validate before calling

// Nothing the caller can pre-validate (pure race), but callers can check first:
bool userExists = await provisioningService.ExistsAsync(user, wasCreated: true, ct);

Try / catch

try { link = await provisioner.CreateLinkOrGetExistingAsync(request, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("deleted while its external identity link was being created"))
{
    link = await provisioner.CreateLinkOrGetExistingAsync(request, ct); // retry: recreates user + link
}

Prevention

When it happens

Trigger: CreateLinkOrGetExistingAsync completes link insertion, but between user creation and the existence re-check another process/request deletes the Elsa user (e.g. admin deletion, cleanup job, cascade from tenant removal). ExistsAsync then returns false and the compensating ExecuteDeleteAsync removes the link before throwing.

Common situations: An administrator deletes the user while the OIDC callback is in flight; a scheduled user-cleanup job races with first-time federated sign-in; duplicate concurrent sign-ins where one request's compensation deletes a shared user row.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/a908858c53b24ed9. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.ExternalAuthentication.Persistence.EFCore/Stores/EFCoreExternalIdentityProvisioner.cs:264

    {
        await using var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
        return await dbContext.ExternalIdentityLinks
            .Where(x => x.Id == linkId && x.TenantId == tenantId)
            .ExecuteDeleteAsync(cancellationToken) > 0;
    }

    private async ValueTask EnsureLinkedUserStillExistsAsync(
        PersistedExternalIdentityLink link,
        User user,
        bool wasCreated,
        CancellationToken cancellationToken)
    {
        if (await _userProvisioningService.ExistsAsync(user, wasCreated, cancellationToken))
            return;

        await using var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
        await dbContext.ExternalIdentityLinks.Where(x => x.Id == link.Id).ExecuteDeleteAsync(cancellationToken);
        throw new InvalidOperationException("The Elsa user was deleted while its external identity link was being created.");
    }

    private async ValueTask EnsureReplacementUserStillExistsAsync(
        PersistedExternalIdentityLink oldLink,
        PersistedExternalIdentityLink replacementLink,
        User replacementUser,
        CancellationToken cancellationToken)
    {
        if (await _userProvisioningService.ExistsAsync(replacementUser, false, cancellationToken))
            return;

        await CompensateReplacementAsync(oldLink, replacementLink, cancellationToken);
        throw new InvalidOperationException("The Elsa user was deleted while its external identity link was being replaced.");
    }

    private async ValueTask CompensateReplacementAsync(
        PersistedExternalIdentityLink oldLink,
        PersistedExternalIdentityLink replacementLink,

View on GitHub (pinned to fe9217bdfa)