microsoft/aspire · warning · InvalidOperationException

Connection string is unavailable

Error message

Connection string is unavailable

What it means

The Npgsql health check registered by AddPostgres resolves its connection string via a factory: connectionString ?? throw new InvalidOperationException("Connection string is unavailable"). If the health check executes before the ConnectionStringAvailableEvent handler cached the value, this exception fails the health check. It is a startup-ordering/state issue, not a Postgres connectivity failure.

Solutions

  1. Let startup complete before relying on health status; check whether the resource actually started.
  2. Fix the connection-string resolution failure (missing username/password parameters or endpoint) surfaced by the earlier event handler.
  3. Bind the health check to the resource's ConnectionStringExpression rather than the cached local.
Defensive patterns

Strategy: try-catch

Try / catch

try { await healthCheckService.CheckHealthAsync(); } catch (InvalidOperationException ex) when (ex.Message == "Connection string is unavailable") { logger.LogWarning("Postgres health check ran before connection string resolved; retry after startup."); }

Prevention

When it happens

Trigger: Health check runs before the connectionString local is populated by the ConnectionStringAvailableEvent callback, or the event never fired because the resource failed to resolve its connection string.

Common situations: Querying health endpoints during AppHost startup; resource start failure preventing connection-string resolution; misconfigured Postgres parameters so the event handler throws first.

Understand the failure class

Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/b31f402685edd6cf. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs:100

            await npgsqlConnection.OpenAsync(ct).ConfigureAwait(false);

            if (npgsqlConnection.State != System.Data.ConnectionState.Open)
            {
                throw new InvalidOperationException($"Could not open connection to '{postgresServer.Name}'");
            }

            foreach (var name in postgresServer.Databases.Keys)
            {
                if (builder.Resources.FirstOrDefault(n => string.Equals(n.Name, name, StringComparisons.ResourceName)) is PostgresDatabaseResource postgreDatabase)
                {
                    await CreateDatabaseAsync(npgsqlConnection, postgreDatabase, @event.Services, ct).ConfigureAwait(false);
                }
            }
        });

        var healthCheckKey = $"{name}_check";
        builder.Services.AddHealthChecks().AddNpgSql(sp => connectionString ?? throw new InvalidOperationException("Connection string is unavailable"), name: healthCheckKey, configure: (connection) =>
        {
            // HACK: The Npgsql client defaults to using the username in the connection string if the database is not specified. Here
            //       we override this default behavior because we are working with a non-database scoped connection string. The Aspirified
            //       package doesn't have to deal with this because it uses a datasource from DI which doesn't have this issue:
            //
            //       https://github.com/npgsql/npgsql/blob/c3b31c393de66a4b03fba0d45708d46a2acb06d2/src/Npgsql/NpgsqlConnection.cs#L445
            //
            connection.ConnectionString += ";Database=postgres;";
        });

        return builder.AddResource(postgresServer)
                      .WithEndpoint(port: port, targetPort: 5432, name: PostgresServerResource.PrimaryEndpointName) // Internal port is always 5432.
                      .WithImage(PostgresContainerImageTags.Image, PostgresContainerImageTags.Tag)
                      .WithImageRegistry(PostgresContainerImageTags.Registry)
                      .WithIconName("DatabaseMultiple")
                      .WithEnvironment("POSTGRES_HOST_AUTH_METHOD", "scram-sha-256")
                      .WithEnvironment("POSTGRES_INITDB_ARGS", "--auth-host=scram-sha-256 --auth-local=scram-sha-256")
                      .WithEnvironment(context =>

View on GitHub (pinned to 25830f84bd)