neondatabase/neon · error · anyhow::Error
Failed to read startup script: {e}
Error message
Failed to read startup script: {e} What it means
On each start, storcon reads base_data_dir/storage_controller_db.startup.sql and executes it in one transaction against its database, then deletes the file. A missing file is fine (the code falls back to 'BEGIN; COMMIT;' to keep the path exercised); any other read error — permission denied, path is a directory, I/O error — aborts the start.
Source
Thrown at control_plane/src/storage_controller.rs:506
let database_url = format!("postgresql://localhost:{postgres_port}/{DB_NAME}");
// We support running a startup SQL script to fiddle with the database before we launch storcon.
// This is used by the test suite.
let startup_script_path = self
.env
.base_data_dir
.join("storage_controller_db.startup.sql");
let startup_script = match tokio::fs::read_to_string(&startup_script_path).await {
Ok(script) => {
tokio::fs::remove_file(startup_script_path).await?;
script
}
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
// always run some startup script so that this code path doesn't bit rot
"BEGIN; COMMIT;".to_string()
} else {
anyhow::bail!("Failed to read startup script: {e}")
}
}
};
let (mut client, conn) = self.connect_to_database(postgres_port).await?;
let conn = tokio::spawn(conn);
let tx = client.build_transaction();
let tx = tx.start().await?;
tx.batch_execute(&startup_script).await?;
tx.commit().await?;
drop(client);
conn.await??;
let addr = format!("{host}:{listen_port}");
let address_for_peers = Uri::builder()
.scheme(scheme)
.authority(addr.clone())
.path_and_query("")
.build()View on GitHub (pinned to 8f60b04da4)
Solutions
- Fix permissions/ownership of storage_controller_db.startup.sql, or remove the file if it is not wanted
- Ensure the path is a regular file, then retry start
Defensive patterns
Strategy: validation
Validate before calling
let p = env.base_data_dir.join("storage_controller_db.startup.sql");
if tokio::fs::try_exists(&p).await? {
let md = tokio::fs::metadata(&p).await?;
anyhow::ensure!(md.is_file(), "startup.sql path is not a regular file");
let _ = tokio::fs::File::open(&p).await?; // probe readability before start
} Prevention
- Write startup.sql atomically (write to a temp file, then rename)
- Fix ownership when scripts run as root but storcon runs unprivileged
- Delete the file after use so stale scripts do not accumulate
When it happens
Trigger: The startup.sql path exists but is unreadable (wrong owner/mode), was accidentally created as a directory, or the filesystem returns an I/O error during read.
Common situations: Test harnesses dropping a startup script with restrictive permissions, root-created files later read by an unprivileged user, half-written files after a crash.
Related errors
- Failed to start postgres {}
- Timed out waiting for postgres to start
- Failed to read storcon pid file at {pid_file:?}: {err}
- pageserver connection information should be provided
- safekeeper connstrings should be provided
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/7a9df0760d85dd49.
Report an issue: GitHub.