elsa-workflows/elsa-core · error · ExternalIdentityUnlinkedException
{reject.SafeReason}
Error message
{reject.SafeReason} What it means
Thrown by DefaultExternalIdentityResolver.ResolveAsync when the selected unlinked identity policy evaluates the sign-in and returns a rejection: the exception surfaces the policy's SafeReason verbatim. It means the policy explicitly declined to link or resolve the external identity (e.g. auto-linking disallowed for this identity/tenant), not an infrastructure failure.
Solutions
- Read the surfaced SafeReason to learn which policy condition rejected the identity.
- Adjust the connection's unlinked identity policy configuration (e.g. enable manual linking or a more permissive policy).
- Have the user complete an explicit link flow instead of relying on automatic resolution.
Defensive patterns
Strategy: fallback
When it happens
Trigger: Thrown at src/modules/Elsa.ExternalAuthentication/Services/DefaultExternalIdentityResolver.cs:42 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/66b3c5dcdc531aeb.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication/Services/DefaultExternalIdentityResolver.cs:42
if (existingLink is not null)
{
ValidateLink(existingLink, context);
return new(existingLink.UserId, false);
}
var selection = GetPolicySelection(context.Connection);
var policy = _policies.GetValueOrDefault(selection.Type)
?? throw new InvalidOperationException($"The unlinked identity policy '{selection.Type}' is not available.");
var decision = await policy.EvaluateAsync(new(
context.TargetTenantId,
context.Connection,
context.Identity,
context.ProjectedClaims,
selection.Settings), cancellationToken);
var result = decision switch
{
UnlinkedIdentityDecision.Reject reject => throw new ExternalIdentityUnlinkedException(reject.SafeReason),
UnlinkedIdentityDecision.CreateUser createUser => await provisioner.CreateLinkOrGetExistingAsync(
new(context.TargetTenantId, connectionKey, context.Identity, createUser.Proposal), cancellationToken),
UnlinkedIdentityDecision.LinkExistingUser linkExistingUser => await provisioner.CreateLinkOrGetExistingAsync(
new(context.TargetTenantId, connectionKey, context.Identity, null, linkExistingUser.UserId), cancellationToken),
_ => throw new InvalidOperationException("The unlinked identity policy returned an unsupported decision.")
};
ValidateLink(result.Link, context);
return new(result.UserId, result.WasCreated);
}
public ValueTask<bool> RecordSuccessfulSignInAsync(
string tenantId,
string connectionKey,
ExternalIdentity identity,
string userId,
DateTimeOffset signedInAt,
CancellationToken cancellationToken = default) =>View on GitHub (pinned to fe9217bdfa)