neondatabase/neon · error · anyhow::Error

createdb failed with status {}: {stderr}

Error message

createdb failed with status {}: {stderr}

What it means

The storage controller's control database is created by spawning the postgres createdb binary against the locally started postgres instance. If createdb exits non-zero and its stderr does not contain 'already exists', the raw exit status and stderr are surfaced with this error.

Source

Thrown at control_plane/src/storage_controller.rs:291

                "-p",
                &format!("{postgres_port}"),
                "-U",
                &username(),
                "-O",
                &username(),
                DB_NAME,
            ])
            .envs(envs)
            .output()
            .await
            .expect("Failed to spawn createdb");

        if !output.status.success() {
            let stderr = String::from_utf8(output.stderr).expect("Non-UTF8 output from createdb");
            if stderr.contains("already exists") {
                tracing::info!("Database {DB_NAME} already exists");
            } else {
                anyhow::bail!("createdb failed with status {}: {stderr}", output.status);
            }
        }

        Ok(database_url)
    }

    pub async fn connect_to_database(
        &self,
        postgres_port: u16,
    ) -> anyhow::Result<(
        tokio_postgres::Client,
        tokio_postgres::Connection<tokio_postgres::Socket, tokio_postgres::tls::NoTlsStream>,
    )> {
        tokio_postgres::Config::new()
            .host("localhost")
            .port(postgres_port)
            // The user is the ambient operating system user name.
            // That is an impurity which we want to fix in => TODO https://github.com/neondatabase/neon/issues/8400

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Read the embedded stderr — createdb states the exact reason (connection refused, auth failed, invalid name)
  2. Confirm postgres is still up on the expected port (pg_isready) right before createdb runs
  3. Fix credentials/pg_hba so createdb can authenticate as the configured user
  4. Drop the inconsistent database and retry start
Defensive patterns

Strategy: validation

Validate before calling

// check the database is absent before running createdb
let exists: bool = client
    .query_opt("SELECT 1 FROM pg_database WHERE datname = $1", &[&DB_NAME])
    .await?
    .is_some();
if exists { /* skip createdb entirely */ }

Try / catch

if !output.status.success() {
    let stderr = String::from_utf8_lossy(&output.stderr);
    if stderr.contains("already exists") { /* fine */ }
    else if stderr.contains("connection refused") || stderr.contains("does not exist") { /* postgres gone: restart and retry */ }
    else { anyhow::bail!("createdb failed: {stderr}"); }
}

Prevention

When it happens

Trigger: createdb cannot reach the server (postgres not accepting connections on postgres_port), authentication fails (pg_hba/missing password), the database name is invalid, or a locale/encoding option is unsupported.

Common situations: postgres exiting between pg_isready succeeding and createdb running, auth configuration drift between storcon and its local postgres, or half-created database state not covered by the 'already exists' string match.

Related errors


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/25667aa794862d28. Report an issue: GitHub.