elsa-workflows/elsa-core · critical · AggregateException
External identity provisioning failed and its just-in-time…
Error message
External identity provisioning failed and its just-in-time user could not be removed. No credentials were issued.
What it means
Thrown by RemoveStrandedUserAsync (called from CreateLinkOrGetExistingAsync's failure path). When creating the external identity link fails after a just-in-time Elsa user was created, the provisioner tries to remove that user; if removal also fails it logs the removal error and throws an AggregateException combining the original link exception and the removal exception. No credentials are issued in this state.
Solutions
- Resolve the inner exceptions: fix the original link-creation failure and the reason user removal failed (constraints, connectivity).
- Manually delete the stranded JIT user (and any partial link rows) so the next sign-in can start clean.
- Retry sign-in once the user store is healthy; provisioning will recreate the user and link.
- Add cascading deletes or cleanup jobs for orphaned JIT users so a failed removal self-heals.
Example fix
// before
var link = await provisioner.CreateLinkOrGetExistingAsync(req, ct);
// after
catch (AggregateException agg) when (agg.Message.Contains("just-in-time user could not be removed"))
{
logger.LogError(agg, "stranded user {UserId} needs manual cleanup", userId);
// clean up stranded user, then retry
} Defensive patterns
Strategy: try-catch
Validate before calling
bool stranded = !await LinkExistsAsync(linkId, ct) && await userExistsInStore(user.Id, ct); // user exists with no link
Try / catch
try { link = await provisioner.CreateLinkOrGetExistingAsync(req, ct); }
catch (AggregateException agg) when (agg.Message.Contains("just-in-time user could not be removed"))
{
foreach (var inner in agg.InnerExceptions) logger.LogError(inner, "Link creation and user cleanup both failed");
// delete the stranded JIT user, then retry sign-in
} Prevention
- Check both inner exceptions to fix link creation AND user removal causes
- Run periodic cleanup for JIT users without identity links
- Ensure the user store permits deletes (no blocking FK/constraints)
- Add idempotent provisioning so a retry after cleanup succeeds cleanly
When it happens
Trigger: Link creation throws (e.g. DB write failure, duplicate link), then _userProvisioningService.RemoveAsync(user) also throws — directory/store outage, dependent rows blocking delete, or cancellation — producing the AggregateException.
Common situations: Database outage during first federated sign-in; FK constraints or concurrent sessions preventing user deletion; the JIT user already removed by a concurrent request; storage provider misconfiguration.
Related errors
- A replacement-compensation link refers to a deleted user…
- AI conversation entity metadata was not found.
- AI conversation table metadata was not found.
- The Elsa user was deleted while its external identity link…
- The Elsa user was deleted while its external identity link…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/5878afff6049d3f9.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication.Persistence.EFCore/Stores/EFCoreExternalIdentityProvisioner.cs:380
catch (Exception cleanupException)
{
throw new AggregateException(
"A replacement-compensation link refers to a deleted user and could not be removed. No credentials were issued.",
operationException,
cleanupException);
}
}
private async ValueTask RemoveStrandedUserAsync(User user, Exception linkException, CancellationToken cancellationToken)
{
try
{
await _userProvisioningService.RemoveAsync(user, cancellationToken);
}
catch (Exception exception)
{
logger.LogError(exception, "Could not remove the just-in-time user {UserId} after its external identity link failed", user.Id);
throw new AggregateException(
"External identity provisioning failed and its just-in-time user could not be removed. No credentials were issued.",
linkException,
exception);
}
}
private static ExternalIdentityLink ToModel(PersistedExternalIdentityLink link) => new(link.Id, link.TenantId, link.ConnectionKey, link.Issuer, link.SubjectHash, link.SubjectHint, link.UserId, link.CreatedAt, link.LastSignedInAt);
}
View on GitHub (pinned to fe9217bdfa)