elsa-workflows/elsa-core · error · ArgumentException

External identity links require a resolved tenant.

Error message

External identity links require a resolved tenant.

What it means

A validation guard thrown by ValidateTargetTenant, which is invoked by every query and mutation of ExternalIdentityLinkManagementService (ListAsync, FindUsersAsync, PrelinkAsync, UnlinkAsync, ReplaceAsync). External identity links are tenant-scoped, so the ambient tenant context must be resolved to a concrete tenant; a null or unresolved tenant id makes the operation ambiguous and is rejected.

Solutions

  1. Run the operation within a resolved tenant context (e.g. via a tenant-scoped API route or ITenantAccessor set beforehand).
  2. Do not call link management endpoints from tenant-agnostic background work; iterate tenants explicitly.
  3. Check the tenant resolver pipeline if callers that should be tenant-scoped arrive unresolved.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at src/modules/Elsa.ExternalAuthentication/Services/ExternalIdentityLinkManagementService.cs:252 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/cdcb7a9925de7f2d. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.ExternalAuthentication/Services/ExternalIdentityLinkManagementService.cs:252

    {
        if (!Uri.TryCreate(issuer?.Trim(), UriKind.Absolute, out var uri) || !string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) || !string.IsNullOrEmpty(uri.Query) || !string.IsNullOrEmpty(uri.Fragment))
            throw new ArgumentException("The issuer must be an absolute HTTPS URI without a query or fragment.", nameof(issuer));
        return uri.GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped).TrimEnd('/');
    }

    private static string NormalizeSubject(string subject)
    {
        var normalized = subject?.Trim();
        if (string.IsNullOrEmpty(normalized) || normalized.Length > 4096)
            throw new ArgumentException("The subject is required and must not exceed 4096 characters.", nameof(subject));
        return normalized;
    }

    private static void ValidateTargetTenant(string tenantId)
    {
        ArgumentNullException.ThrowIfNull(tenantId);
        if (string.Equals(tenantId, ConnectionScope.HostTenantId, StringComparison.Ordinal))
            throw new ArgumentException("External identity links require a resolved tenant.", nameof(tenantId));
    }
}

public sealed record ExternalIdentityLinkUser(string Id, string DisplayName);

public abstract record ExternalIdentityLinkPrelinkResult
{
    private ExternalIdentityLinkPrelinkResult() { }
    public sealed record Success(ExternalIdentityLink Link, bool WasCreated) : ExternalIdentityLinkPrelinkResult;
    public sealed record Conflict(ExternalIdentityLink Link) : ExternalIdentityLinkPrelinkResult;
    public sealed record UserNotFound : ExternalIdentityLinkPrelinkResult;
    public sealed record ConnectionNotFound : ExternalIdentityLinkPrelinkResult;
}

View on GitHub (pinned to fe9217bdfa)