elsa-workflows/elsa-core · error · InvalidOperationException
The secret binding selects a different resolver type.
Error message
The secret binding selects a different resolver type.
What it means
EnsureResolverType verifies that the SecretBinding's ResolverType exactly matches the resolver being asked to handle it (ordinal comparison). When the binding was registered for a different resolver implementation, this resolver throws rather than silently resolving a secret it does not own. This is a routing/misconfiguration guard inside ElsaSecretBindingResolver.
Solutions
- Re-stage the secret with the currently registered managed secret writer so the binding's ResolverType matches the active resolver
- Check binding.ResolverType against the resolver registered in DI and align configuration (connection SecretBindings) with it
- If multiple resolver types legitimately coexist, ensure lookups go through the resolver registry that dispatches by ResolverType rather than calling this resolver directly
- Fix typos in hand-written binding definitions — the comparison is ordinal, so exact casing matters
Example fix
// before: stale binding from a different resolver
connection.SecretBindings[field] = new SecretBinding { ResolverType = "LegacyWriter", Reference = "ref-1" };
// after: re-stage with the current writer
var staged = await writer.StageAsync(new(connection.Id, field, value), ct);
connection.SecretBindings[field] = staged; // ResolverType matches current resolver Defensive patterns
Strategy: validation
Validate before calling
// before resolving
if (!string.Equals(binding.ResolverType, expectedResolverType, StringComparison.Ordinal))
throw new InvalidOperationException($"Binding resolver type '{binding.ResolverType}' does not match '{expectedResolverType}'; re-stage the secret."); Type guard
bool IsOwnedBy(SecretBinding b, string resolverType) => string.Equals(b.ResolverType, resolverType, StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(b.Reference);
Try / catch
try { var value = await resolver.ResolveAsync(binding, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("different resolver type"))
{ logger.LogError("Binding {Reference} belongs to another resolver ({Type}).", binding.Reference, binding.ResolverType); } Prevention
- Never hand-write SecretBinding entries; stage them through the registered writer
- When switching secret writers, re-stage all connection secrets and migrate bindings
- Dispatch through the resolver registry by ResolverType instead of calling a specific resolver directly
- Keep ResolverType strings in a shared constant to avoid casing/typo drift
When it happens
Trigger: Calling GetStateAsync, ResolveAsync, or RemoveAsync on the ElsaSecretBindingResolver with a SecretBinding whose binding.ResolverType differs (even by case or whitespace variant) from this resolver's ResolverType constant — typically because the connection's SecretBindings dictionary holds a binding produced by another managed secret writer.
Common situations: Switching secret writer/resolver implementations after connections were saved, so old bindings still name the previous ResolverType; hand-crafted SecretBinding entries in configuration with a typo'd ResolverType; copying bindings between environments where different resolver types are registered.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- The configured secret binding could not be resolved.
- The secret binding reference is required.
- Configuration connection
- The configured secret binding could not be resolved.
- The configured secret binding is invalid.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/3f1a542098b6ad41.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication.Secrets/Services/ElsaSecretBindingResolver.cs:88
var secret = await secretManager.GetAsync(binding.Reference, cancellationToken)
?? throw new InvalidOperationException("The configured secret binding could not be resolved.");
if (!IsCompatible(secret, binding))
throw new InvalidOperationException("The configured secret binding is incompatible with the required type or scope.");
if (secret is not { Status: SecretStatus.Active, LatestActiveVersion: { } version })
throw new InvalidOperationException("The configured secret binding is not active.");
var payload = await secretManager.ResolvePayloadAsync(secret, cancellationToken);
if (payload.Value is null)
throw new InvalidOperationException("The configured secret binding could not be resolved.");
var fingerprint = handleHasher.Hash($"{ResolverType}:{secret.Id}:{version.Version}:{version.CreatedAt.ToUnixTimeMilliseconds()}");
return new(new(payload.Value), fingerprint);
}
private static void EnsureResolverType(SecretBinding binding)
{
if (!string.Equals(binding.ResolverType, ResolverType, StringComparison.Ordinal))
throw new InvalidOperationException("The secret binding selects a different resolver type.");
if (string.IsNullOrWhiteSpace(binding.Reference))
throw new InvalidOperationException("The secret binding reference is required.");
}
private static bool IsCompatible(Secret secret, SecretBinding binding) =>
(string.IsNullOrWhiteSpace(binding.ExpectedType) || string.Equals(secret.TypeName, binding.ExpectedType, StringComparison.OrdinalIgnoreCase)) &&
(string.IsNullOrWhiteSpace(binding.ExpectedScope) || string.Equals(secret.Scope, binding.ExpectedScope, StringComparison.OrdinalIgnoreCase));
}
View on GitHub (pinned to fe9217bdfa)