datahaven-xyz/datahaven · critical
failed creating sql backend
Error message
failed creating sql backend: {:?} What it means
This panic occurs in open_frontier_backend when fc_db::Backend::Sql fails to initialize the Frontier SQL backend (used by the EVM RPC layer to expose blockchain data over SQL). The backend needs a working database connection, a valid cache size, pool size, and operation timeout; any failure there is unrecoverable at node startup, so the code panics via unwrap_or_else. Since it is a panic (not a Result), the node process aborts during startup.
Solutions
- Verify the SQL database (e.g. PostgreSQL) is running and reachable with the configured connection settings.
- Check that frontier_sql_backend_pool_size and frontier_sql_backend_num_ops_timeout are non-zero positive integers (they are converted via NonZeroU32).
- Review the debug-printed error ({:?}) in the panic message for the underlying cause (connection refused, auth failure, schema error).
- If the schema is incompatible after a Frontier upgrade, recreate/migrate the database schema.
- If SQL backend is not needed, switch the CLI config to the in-memory (KeyValue) frontier backend instead.
Example fix
// before eth_config.frontier_sql_backend_pool_size = 0; // after eth_config.frontier_sql_backend_pool_size = 5; // must be a non-zero u32
Defensive patterns
Strategy: try-catch
Validate before calling
// preflight: check DB reachable and settings sane before building the node
if (pool_size == 0 || num_ops_timeout == 0) panic!("frontier sql pool size and ops timeout must be non-zero");
TcpStream::connect((db_host, db_port)).expect("frontier SQL backend database unreachable"); Type guard
fn valid_sql_backend_config(cache: u32, pool: u32, timeout: u32) -> bool {
NonZeroU32::new(pool).is_some() && NonZeroU32::new(timeout).is_some() && cache > 0
} Try / catch
// panic is fatal; catch at process supervisor level
match open_frontier_backend(&config) {
Ok(backend) => backend,
Err(e) => { log::error!("frontier sql backend init failed: {:?}", e); std::process::exit(1); }
} Prevention
- Health-check the database as a startup dependency before launching the node.
- Validate pool size and timeout are positive in CLI config parsing.
- Log the underlying error before panicking so operators see the root cause.
- Pin Frontier versions and rerun schema migrations after upgrades.
When it happens
Trigger: Calling new_partial (and thus open_frontier_backend) when the frontier-sql backend cannot be created: database unreachable/wrong connection settings, invalid frontier_sql_backend_cache_size, frontier_sql_backend_pool_size, or frontier_sql_backend_num_ops_timeout values, or incompatible schema/overrides.
Common situations: Running the node with frontier-sql enabled but the database service down or misconfigured (wrong host/port/credentials), pool_size or timeout set to zero or overflowing NonZeroU32 values, or stale SQL schema after upgrading Frontier versions.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13).
Data as JSON: /api/errors/40d6e4f16e70d924.
Report an issue: GitHub.
Appendix: source
Thrown at operator/node/src/service.rs:255
let overrides = Arc::new(StorageOverrideHandler::new(client.clone()));
let sqlite_db_path = frontier_database_dir(config, "sql");
std::fs::create_dir_all(&sqlite_db_path).expect("failed creating sql db directory");
let backend = futures::executor::block_on(fc_db::sql::Backend::new(
fc_db::sql::BackendConfig::Sqlite(fc_db::sql::SqliteBackendConfig {
path: Path::new("sqlite:///")
.join(sqlite_db_path)
.join("frontier.db3")
.to_str()
.expect("frontier sql path error"),
create_if_missing: true,
thread_count: eth_config.frontier_sql_backend_thread_count,
cache_size: eth_config.frontier_sql_backend_cache_size,
}),
eth_config.frontier_sql_backend_pool_size,
std::num::NonZeroU32::new(eth_config.frontier_sql_backend_num_ops_timeout),
overrides.clone(),
))
.unwrap_or_else(|err| panic!("failed creating sql backend: {:?}", err));
fc_db::Backend::Sql(Arc::new(backend))
}
};
Ok(frontier_backend)
}
fn build_babe_inherent_providers(
slot_duration: sp_consensus_babe::SlotDuration,
use_mock_timestamp: bool,
) -> (
sp_consensus_babe::inherents::InherentDataProvider,
sp_timestamp::InherentDataProvider,
) {
if use_mock_timestamp {
// In manual/instant sealing we want to advance time deterministically per block
// to satisfy `pallet_timestamp` MinimumPeriod without sleeping. We increment a
// static counter by one slot each time and use that value as the timestamp.View on GitHub (pinned to edcb13dbbc)