elsa-workflows/elsa-core · error · InvalidOperationException
The secret binding resolver is unavailable.
Error message
The secret binding resolver is unavailable.
What it means
ConnectionTestService resolves each SecretBinding via a dictionary of registered ISecretBindingResolver implementations keyed by resolver Type. When a binding's ResolverType has no matching registered resolver, the test cannot obtain the secret and throws this error; the surrounding catch in TestAsync converts it into a failed connection observation.
Solutions
- Register the ISecretBindingResolver implementation matching binding.ResolverType in the DI container.
- Add the Elsa ExternalAuthentication feature/module that provides the missing resolver at startup.
- Check the stored connection's SecretBindings for a typo'd or obsolete ResolverType and correct it.
- Verify the resolver type name is identical across environments where the connection is used.
Example fix
// before
services.AddExternalAuthentication(); // no resolvers registered
// after
services.AddExternalAuthentication()
.AddConfigurationSecretBindingResolver(); // registers resolver for "Configuration" type Defensive patterns
Strategy: validation
Validate before calling
// At startup or before testing, check every binding has a registered resolver
var resolverTypes = secretBindingResolvers.Select(r => r.Type).ToHashSet(StringComparer.Ordinal);
foreach (var (name, binding) in connection.SecretBindings)
if (!resolverTypes.Contains(binding.ResolverType))
throw new InvalidOperationException($"No secret binding resolver registered for type '{binding.ResolverType}' (binding '{name}')."); Try / catch
try
{
await testService.TestAsync(connectionId, revision, tenantId, actor, ct);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("resolver is unavailable"))
{
logger.LogWarning("Connection test skipped: resolver missing for binding type.");
} Prevention
- Register all needed ISecretBindingResolver implementations in every environment's DI setup.
- Run a startup health check that resolves types for all stored connections.
- Keep resolver type strings as constants shared between registration and stored data.
- Deploy resolver-providing packages consistently across environments.
When it happens
Trigger: Thrown from ResolveSecretsAsync (called by TestAsync) when connection.Connection.SecretBindings contains an entry whose binding.ResolverType is not present in the registered resolver dictionary — i.e. the resolver for that type string was never registered in DI.
Common situations: The feature/package providing the resolver (e.g. configuration or a vault resolver) is not registered in the host; a typo or renamed resolver type in stored connection data; a custom resolver type referenced by connections but deployed to only some environments.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- Register with configured before calling , or call with a…
- The console log provider registration is invalid.
- The configured secret binding could not be resolved.
- The secret binding selects a different resolver type.
- The secret binding reference is required.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/f1c365f4fbb71b28.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication/Services/ConnectionTestService.cs:88
null,
observation.Status == ConnectionObservationStatus.Failed ? SecurityEventOutcome.Failed : SecurityEventOutcome.Succeeded,
"Identity provider connection test completed."),
observation.TestedMaterialRevision,
observation.Status.ToString().ToLowerInvariant(),
observation.Category,
observation.Duration), cancellationToken);
return new ConnectionTestOperationResult.Completed(observation);
}
private async ValueTask<IReadOnlyDictionary<string, ResolvedSecretBinding>> ResolveSecretsAsync(IDictionary<string, SecretBinding> bindings, CancellationToken cancellationToken)
{
var result = new Dictionary<string, ResolvedSecretBinding>(StringComparer.Ordinal);
try
{
foreach (var (name, binding) in bindings)
{
if (!_resolvers.TryGetValue(binding.ResolverType, out var resolver))
throw new InvalidOperationException("The secret binding resolver is unavailable.");
result[name] = await resolver.ResolveAsync(binding, cancellationToken);
}
return result;
}
catch
{
foreach (var secret in result.Values)
secret.Value.Dispose();
throw;
}
}
private static string? ActorId(ClaimsPrincipal actor) => actor.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? actor.FindFirst("sub")?.Value;
// Adapter messages are already contractually safe, but cap them at a predictable diagnostic size.
private static string SafeSummary(string value) => string.IsNullOrWhiteSpace(value) ? "No additional details are available." : value.Length <= 512 ? value : value[..512];
private static string SafeCategory(string value) => string.IsNullOrWhiteSpace(value) ? "unknown" : value.Length <= 128 ? value : "unknown";
}
View on GitHub (pinned to fe9217bdfa)