elsa-workflows/elsa-core · error · InvalidOperationException

The unlinked identity policy

Error message

The unlinked identity policy '{selection.Type}' is not available.

What it means

Thrown by DefaultExternalIdentityResolver.ResolveAsync when the connection's configured unlinked-identity policy selection references a policy Type that is not present in the dictionary of IUnlinkedIdentityPolicy implementations resolved from DI. It is a configuration guard: the connection was set up with a policy type no registered provider implements.

Solutions

  1. Register the missing IUnlinkedIdentityPolicy implementation (matching selection.Type) in the DI container.
  2. Update the connection configuration to select a policy type that is registered.
  3. Check for typos or casing mismatches between the configured policy type string and the registered policy's Type value.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at src/modules/Elsa.ExternalAuthentication/Services/DefaultExternalIdentityResolver.cs:32 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/9f9b566c5956ad20. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.ExternalAuthentication/Services/DefaultExternalIdentityResolver.cs:32

    IOptions<ExternalAuthenticationOptions> options) : IExternalIdentityResolver, IExternalIdentitySignInTracker
{
    private readonly IReadOnlyDictionary<string, IUnlinkedIdentityPolicy> _policies = policies.ToDictionary(x => x.Type, StringComparer.Ordinal);

    public async ValueTask<ExternalIdentityResolution> ResolveAsync(ExternalIdentityResolutionContext context, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(context);

        var connectionKey = ConnectionRevisionCalculator.NormalizeKey(context.Connection.Connection.Key);
        var existingLink = await provisioner.FindLinkAsync(context.TargetTenantId, connectionKey, context.Identity, cancellationToken);
        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);

View on GitHub (pinned to fe9217bdfa)