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

  1. Read the surfaced SafeReason to learn which policy condition rejected the identity.
  2. Adjust the connection's unlinked identity policy configuration (e.g. enable manual linking or a more permissive policy).
  3. 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)