{"record":{"id":"14cf9d0083a6469f","repo":"microsoft/aspire","slug":"connection-string-is-unavailable-valkeybuilderextensions","errorCode":null,"errorMessage":"Connection string is unavailable","messagePattern":"Connection string is unavailable","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Valkey/ValkeyBuilderExtensions.cs","lineNumber":142,"sourceCode":"        var passwordParameter = password?.Resource ?? ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(builder, $\"{name}-password\", special: false);\n\n        var valkey = new ValkeyResource(name, passwordParameter);\n\n        string? connectionString = null;\n\n        builder.Eventing.Subscribe<ConnectionStringAvailableEvent>(valkey, async (@event, ct) =>\n        {\n            connectionString = await valkey.ConnectionStringExpression.GetValueAsync(ct).ConfigureAwait(false);\n\n            if (connectionString is null)\n            {\n                throw new DistributedApplicationException($\"ConnectionStringAvailableEvent was published for the '{valkey.Name}' resource but the connection string was null.\");\n            }\n        });\n\n        var healthCheckKey = $\"{name}_check\";\n        builder.Services.AddHealthChecks()\n            .AddRedis(sp => connectionString ?? throw new InvalidOperationException(\"Connection string is unavailable\"), name: healthCheckKey);\n\n        return builder.AddResource(valkey)\n            .WithEndpoint(port: port, targetPort: 6379, name: ValkeyResource.PrimaryEndpointName)\n            .WithImage(ValkeyContainerImageTags.Image, ValkeyContainerImageTags.Tag)\n            .WithImageRegistry(ValkeyContainerImageTags.Registry)\n            .WithIconName(\"Database\")\n            .WithHealthCheck(healthCheckKey)\n            // see https://github.com/microsoft/aspire/issues/3838 for why the password is passed this way\n            .WithEntrypoint(\"/bin/sh\")\n            .WithEnvironment(context =>\n            {\n                if (valkey.PasswordParameter is { } password)\n                {\n                    context.EnvironmentVariables[\"VALKEY_PASSWORD\"] = password;\n                }\n            })\n            .WithArgs(context =>\n            {","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Valkey/ValkeyBuilderExtensions.cs#L124-L160","documentation":"AddValkey registers a Redis health check whose factory lazily reads the captured connectionString variable. If the health check runs before the ConnectionStringAvailableEvent handler populated it, the factory throws this InvalidOperationException instead of silently failing health checks.","triggerScenarios":"Health check executes (e.g. during app start or a /health request) while connectionString is still null — the ConnectionStringAvailableEvent either never fired or fired after the health check was invoked.","commonSituations":"Health checks polled very early at startup; the Valkey resource failed to start so the event never fired; tests hitting the health endpoint before resources are started; resource in error state blocking connection string availability.","solutions":["Ensure the Valkey resource starts successfully; check dashboard/logs for resource startup errors so the connection string event fires.","Wait for the resource to reach Running state (or use WaitFor on dependents) before invoking health checks.","In tests, await app.ResourceNotifications.WaitForResourceHealthyAsync(\"cache\") before probing health endpoints.","If the error persists, verify no custom code is suppressing the ConnectionStringAvailableEvent subscription."],"exampleFix":"// before (test)\nvar response = await client.GetAsync(\"/health\");\n// after\nawait app.ResourceNotifications.WaitForResourceHealthyAsync(\"cache\");\nvar response = await client.GetAsync(\"/health\");","handlingStrategy":"retry","validationCode":"await app.ResourceNotifications.WaitForResourceHealthyAsync(\"cache\"); // before hitting health endpoints","typeGuard":null,"tryCatchPattern":"try { var rsp = await client.GetAsync(\"/health\"); rsp.EnsureSuccessStatusCode(); }\ncatch (InvalidOperationException ex) when (ex.Message == \"Connection string is unavailable\")\n{\n    // resource not started yet; wait and retry\n}","preventionTips":["Wait for resources to become healthy before probing health endpoints.","Check the dashboard for resource startup failures.","Use WaitFor in dependents to order startup."],"tags":["valkey","health-check","connection-string","aspire"],"backgroundTag":"missing-config-value","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}