nautechsystems/nautilus_trader · error
Failed to insert instrument close: {e}
Error message
Failed to insert instrument close: {e} What it means
Raised by `add_instrument_close` when the INSERT (or upsert) of an `InstrumentClose` record into the `instrument_close` table fails. The library maps the sqlx `execute` error into an `anyhow::Error` with this message. It indicates the close record was not persisted, so downstream replay/backtest data will be incomplete for that instrument.
Source
Thrown at crates/infrastructure/src/sql/queries.rs:271
INSERT INTO "instrument_close" (
instrument_id, close_price, close_type, ts_event, ts_init, created_at
) VALUES ($1, $2, $3, $4, $5, CURRENT_TIMESTAMP)
ON CONFLICT (instrument_id) DO UPDATE
SET close_price = EXCLUDED.close_price,
close_type = EXCLUDED.close_type,
ts_event = EXCLUDED.ts_event,
ts_init = EXCLUDED.ts_init
"#,
)
.bind(close.instrument_id.to_string())
.bind(close.close_price.to_string())
.bind(close.close_type.to_string())
.bind(close.ts_event.to_string())
.bind(close.ts_init.to_string())
.execute(pool)
.await
.map(|_| ())
.map_err(|e| anyhow::anyhow!("Failed to insert instrument close: {e}"))
}
/// Loads all `InstrumentClose` entries.
///
/// # Errors
///
/// Returns an error if the SQL SELECT or row decoding fails.
pub async fn load_instrument_closes(pool: &PgPool) -> anyhow::Result<Vec<InstrumentClose>> {
sqlx::query_as::<_, InstrumentCloseRow>(
"SELECT * FROM instrument_close ORDER BY instrument_id ASC",
)
.fetch_all(pool)
.await
.map(|rows| rows.into_iter().map(|row| row.0).collect())
.map_err(|e| anyhow::anyhow!("Failed to load instrument closes: {e}"))
}
/// Inserts an `OrderInitialized` event via the provided `pool`.View on GitHub (pinned to 18893faf8b)
Solutions
- Check the wrapped `{e}` for a constraint violation or missing-table message.
- Apply the schema migrations to ensure `instrument_close` exists with expected columns.
- Deduplicate or use upsert semantics if re-ingesting the same instrument close data.
- Verify the connection pool is healthy and retry transient connection failures.
Example fix
// before: ignores failure mode, keeps going
add_instrument_close(&pool, close).await?;
// after: detect duplicate-key errors specifically and retry/skip
if let Err(e) = add_instrument_close(&pool, close).await {
let msg = format!("{e:#}");
if msg.contains("duplicate key") {
tracing::warn!("instrument close already exists, skipping");
} else {
return Err(e);
}
} Defensive patterns
Strategy: validation
Validate before calling
// Ensure the table exists and this close is not already persisted
let exists = sqlx::query_scalar::<_, i64>(
"SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'instrument_close'")
.fetch_one(pool).await? > 0;
let dup = exists && sqlx::query_scalar::<_, i64>(
"SELECT COUNT(*) FROM instrument_close WHERE instrument_id = $1 AND ts_init = $2")
.bind(close.instrument_id.to_string())
.bind(close.ts_init.to_string())
.fetch_one(pool).await? > 0;
if dup { return Ok(()); } Try / catch
if let Err(e) = add_instrument_close(&pool, &close).await {
let msg = format!("{e:#}");
if msg.contains("duplicate key") { tracing::debug!("skipping duplicate close"); }
else { return Err(e); }
} Prevention
- Make ingestion idempotent (ON CONFLICT DO NOTHING/UPDATE) when re-running jobs.
- Apply migrations before any write path executes.
- Confirm close_type/ts string formats match the target column types.
- Monitor pool health for long batch writes.
When it happens
Trigger: Calling `add_instrument_close(pool, close)` when: the `instrument_close` table does not exist; a bound value violates a column constraint (NOT NULL, unique/PK conflict on instrument_id+ts); a column type mismatches the bound string representation; or the connection drops mid-execution.
Common situations: Writing to a database where migrations were not applied; duplicate instrument-close rows on re-running an ingestion job without ON CONFLICT semantics; timestamps or close_type formatting rejected by column types; connection pool closed during long batch writes.
Related errors
- Failed to insert into block table: {e}
- Failed to batch insert into block table: {e}
- Failed to batch insert into pool_event_block table: {e}
- Failed to insert into dex table: {e}
- Failed to insert into pool table: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/3bc205b9958b7d8d.
Report an issue: GitHub.