{"record":{"id":"f1c365f4fbb71b28","repo":"elsa-workflows/elsa-core","slug":"the-secret-binding-resolver-is-unavailable","errorCode":null,"errorMessage":"The secret binding resolver is unavailable.","messagePattern":"The secret binding resolver is unavailable\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.ExternalAuthentication/Services/ConnectionTestService.cs","lineNumber":88,"sourceCode":"                null,\n                observation.Status == ConnectionObservationStatus.Failed ? SecurityEventOutcome.Failed : SecurityEventOutcome.Succeeded,\n                \"Identity provider connection test completed.\"),\n            observation.TestedMaterialRevision,\n            observation.Status.ToString().ToLowerInvariant(),\n            observation.Category,\n            observation.Duration), cancellationToken);\n        return new ConnectionTestOperationResult.Completed(observation);\n    }\n\n    private async ValueTask<IReadOnlyDictionary<string, ResolvedSecretBinding>> ResolveSecretsAsync(IDictionary<string, SecretBinding> bindings, CancellationToken cancellationToken)\n    {\n        var result = new Dictionary<string, ResolvedSecretBinding>(StringComparer.Ordinal);\n        try\n        {\n            foreach (var (name, binding) in bindings)\n            {\n                if (!_resolvers.TryGetValue(binding.ResolverType, out var resolver))\n                    throw new InvalidOperationException(\"The secret binding resolver is unavailable.\");\n                result[name] = await resolver.ResolveAsync(binding, cancellationToken);\n            }\n            return result;\n        }\n        catch\n        {\n            foreach (var secret in result.Values)\n                secret.Value.Dispose();\n            throw;\n        }\n    }\n\n    private static string? ActorId(ClaimsPrincipal actor) => actor.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? actor.FindFirst(\"sub\")?.Value;\n    // Adapter messages are already contractually safe, but cap them at a predictable diagnostic size.\n    private static string SafeSummary(string value) => string.IsNullOrWhiteSpace(value) ? \"No additional details are available.\" : value.Length <= 512 ? value : value[..512];\n    private static string SafeCategory(string value) => string.IsNullOrWhiteSpace(value) ? \"unknown\" : value.Length <= 128 ? value : \"unknown\";\n}\n","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.ExternalAuthentication/Services/ConnectionTestService.cs#L70-L106","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nservices.AddExternalAuthentication(); // no resolvers registered\n// after\nservices.AddExternalAuthentication()\n        .AddConfigurationSecretBindingResolver(); // registers resolver for \"Configuration\" type","handlingStrategy":"validation","validationCode":"// At startup or before testing, check every binding has a registered resolver\nvar resolverTypes = secretBindingResolvers.Select(r => r.Type).ToHashSet(StringComparer.Ordinal);\nforeach (var (name, binding) in connection.SecretBindings)\n    if (!resolverTypes.Contains(binding.ResolverType))\n        throw new InvalidOperationException($\"No secret binding resolver registered for type '{binding.ResolverType}' (binding '{name}').\");","typeGuard":null,"tryCatchPattern":"try\n{\n    await testService.TestAsync(connectionId, revision, tenantId, actor, ct);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"resolver is unavailable\"))\n{\n    logger.LogWarning(\"Connection test skipped: resolver missing for binding type.\");\n}","preventionTips":["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."],"tags":["dependency-injection","secrets","configuration","resolver"],"backgroundTag":"missing-dependency","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}