nautechsystems/nautilus_trader · error · anyhow::Error
Failed to write pool data: {e}
Error message
Failed to write pool data: {e} What it means
write_pool_binary serializes a single pool row in PostgreSQL COPY binary format and sends it through the copy_in sink; failures are wrapped in this anyhow error. NULL fields are encoded as -1 i32 length prefixes, so errors here typically indicate sink/connection issues rather than value problems.
Source
Thrown at crates/adapters/blockchain/src/cache/copy.rs:902
if let Some(ref initial_sqrt_price) = pool.initial_sqrt_price_x96 {
write_copy_numeric(&mut row_data, initial_sqrt_price);
} else {
row_data.write_all(&(-1i32).to_be_bytes())?; // NULL
}
if let Some(ref hooks) = pool.hooks {
let hooks_bytes = hooks.to_string().as_bytes().to_vec();
row_data.write_all(&(hooks_bytes.len() as i32).to_be_bytes())?;
row_data.write_all(&hooks_bytes)?;
} else {
row_data.write_all(&(-1i32).to_be_bytes())?; // NULL
}
copy_in
.send(row_data)
.await
.map_err(|e| anyhow::anyhow!("Failed to write pool data: {e}"))?;
Ok(())
}
/// Writes the PostgreSQL COPY binary format trailer.
///
/// The trailer is a 2-byte value of -1 to indicate end of data.
async fn write_copy_trailer(
&self,
copy_in: &mut sqlx::postgres::PgCopyIn<sqlx::pool::PoolConnection<sqlx::Postgres>>,
) -> anyhow::Result<()> {
// Binary trailer: -1 as i16 to indicate end of data
let trailer = (-1i16).to_be_bytes();
copy_in
.send(trailer.to_vec())
.await
.map_err(|e| anyhow::anyhow!("Failed to write COPY trailer: {e}"))?;
Ok(())
}View on GitHub (pinned to 18893faf8b)
Solutions
- Check the wrapped source error to distinguish connectivity from encoding problems
- Confirm the pools table schema still matches write_pool_binary's field order and types
- Keep the connection/pool alive for the duration of the COPY (no early drops, adequate timeouts)
- Re-run copy_pools; the aborted COPY is rolled back so retrying is safe
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure pool table exists with expected columns before copy
sqlx::query("SELECT 1 FROM information_schema.tables WHERE table_name='pool'")
.fetch_optional(&pool).await?
.ok_or_else(|| anyhow::anyhow!("pool table missing; run migrations"))?; Try / catch
async fn copy_pools_guarded(db: &CacheDatabase, chain: &Chain, pools: &[Pool]) -> anyhow::Result<()> {
db.copy_pools(chain, pools).await.map_err(|e| {
anyhow::anyhow!("copy_pools failed (partitions created? schema current?): {e:#}")
})
} Prevention
- Call create_block_partition/create_token_partition during cache init, before any copy
- Keep the hard-coded binary field order in sync with schema migrations
- Avoid dropping the connection pool while copy operations are in flight
- Set generous statement timeouts for bulk load sessions
When it happens
Trigger: copy_in.send(row_data) fails during copy_pools — the PostgreSQL connection dropped, the server aborted the COPY stream, or the row bytes no longer match the pool table's binary column layout.
Common situations: Long-running pool cache sync killed by network timeout; DDL change on the pool table invalidating the hard-coded binary field order; database connection pool closed by the application early.
Related errors
- Failed to write pool fee collect data: {e}
- Failed to write token data: {e}
- Failed to start COPY operation: {e}
- Failed to finish COPY operation: {e}
- Failed to write COPY trailer: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a7f9009c5fdfc80b.
Report an issue: GitHub.