{"record":{"id":"c2ac574735562487","repo":"microsoft/aspire","slug":"connection-string-is-unavailable-redisbuilderextensions","errorCode":null,"errorMessage":"Connection string is unavailable","messagePattern":"Connection string is unavailable","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs","lineNumber":93,"sourceCode":"        // https://github.com/Azure/azure-dev/issues/4848\n        var passwordParameter = password?.Resource ?? ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(builder, $\"{name}-password\", special: false);\n\n        var redis = new RedisResource(name, passwordParameter);\n\n        string? connectionString = null;\n\n        builder.Eventing.Subscribe<ConnectionStringAvailableEvent>(redis, async (@event, ct) =>\n        {\n            connectionString = await redis.GetConnectionStringAsync(ct).ConfigureAwait(false);\n\n            if (connectionString == null)\n            {\n                throw new DistributedApplicationException($\"ConnectionStringAvailableEvent was published for the '{redis.Name}' resource but the connection string was null.\");\n            }\n        });\n\n        var healthCheckKey = $\"{name}_check\";\n        builder.Services.AddHealthChecks().AddRedis(sp => connectionString ?? throw new InvalidOperationException(\"Connection string is unavailable\"), name: healthCheckKey);\n\n        var redisBuilder = builder.AddResource(redis)\n            .WithEndpoint(port: port, targetPort: 6379, name: RedisResource.PrimaryEndpointName, scheme: RedisResource.StandardRedisScheme)\n            .WithImage(RedisContainerImageTags.Image, RedisContainerImageTags.Tag)\n            .WithImageRegistry(RedisContainerImageTags.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 (redis.PasswordParameter is { } password)\n                {\n                    context.EnvironmentVariables[\"REDIS_PASSWORD\"] = password;\n                }\n            })\n            .WithArgs(context =>\n            {","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs#L75-L111","documentation":"AddRedis registers a health check whose factory reads the captured connection string; if it is still unavailable when the health check executes, the factory throws InvalidOperationException('Connection string is unavailable'). This surfaces when the health check runs before the ConnectionStringAvailableEvent populated the string, or when connection string resolution failed.","triggerScenarios":"The '{name}_check' Redis health check executes while the closure variable connectionString is null — the health-check callback runs before connection string publication completed.","commonSituations":"App host startup racing health checks against connection string publication, a hung/failed connection-string resolution (e.g. container not started), or tests hitting health endpoints before the app model is ready.","solutions":["Ensure the Redis resource (or its dependency chain) starts and publishes its connection string before health checks run.","Check app host logs for connection string resolution failures or the sibling DistributedApplicationException from the event subscription.","If running in tests, wait for the resource to reach Running/Healthy state before invoking health endpoints."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Caller-side: only query health endpoints after the resource is running.\n// await app.ResourceNotifications.WaitForResourceHealthyAsync(\"redis\");","typeGuard":null,"tryCatchPattern":"try { await httpClient.GetAsync(\"/health\"); } catch (InvalidOperationException ex) when (ex.Message == \"Connection string is unavailable\") { await Task.Delay(TimeSpan.FromSeconds(1)); // retry until the connection string is published }","preventionTips":["Gate health-check consumption on resource state (Running/Healthy) rather than timing.","Investigate root-cause logs if connection string publication is delayed — often the container failed to start.","In tests, use WaitForResource / WaitForResourceHealthyAsync before hitting health endpoints."],"tags":["redis","health-check","connection-string","aspire"],"backgroundTag":"connection-string-unavailable","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"}