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

  1. Verify the SQL database (e.g. PostgreSQL) is running and reachable with the configured connection settings.
  2. Check that frontier_sql_backend_pool_size and frontier_sql_backend_num_ops_timeout are non-zero positive integers (they are converted via NonZeroU32).
  3. Review the debug-printed error ({:?}) in the panic message for the underlying cause (connection refused, auth failure, schema error).
  4. If the schema is incompatible after a Frontier upgrade, recreate/migrate the database schema.
  5. 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

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)