{"record":{"id":"3bc205b9958b7d8d","repo":"nautechsystems/nautilus_trader","slug":"failed-to-insert-instrument-close-e","errorCode":null,"errorMessage":"Failed to insert instrument close: {e}","messagePattern":"Failed to insert instrument close: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/infrastructure/src/sql/queries.rs","lineNumber":271,"sourceCode":"            INSERT INTO \"instrument_close\" (\n                instrument_id, close_price, close_type, ts_event, ts_init, created_at\n            ) VALUES ($1, $2, $3, $4, $5, CURRENT_TIMESTAMP)\n            ON CONFLICT (instrument_id) DO UPDATE\n            SET close_price = EXCLUDED.close_price,\n                close_type = EXCLUDED.close_type,\n                ts_event = EXCLUDED.ts_event,\n                ts_init = EXCLUDED.ts_init\n            \"#,\n        )\n        .bind(close.instrument_id.to_string())\n        .bind(close.close_price.to_string())\n        .bind(close.close_type.to_string())\n        .bind(close.ts_event.to_string())\n        .bind(close.ts_init.to_string())\n        .execute(pool)\n        .await\n        .map(|_| ())\n        .map_err(|e| anyhow::anyhow!(\"Failed to insert instrument close: {e}\"))\n    }\n\n    /// Loads all `InstrumentClose` entries.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the SQL SELECT or row decoding fails.\n    pub async fn load_instrument_closes(pool: &PgPool) -> anyhow::Result<Vec<InstrumentClose>> {\n        sqlx::query_as::<_, InstrumentCloseRow>(\n            \"SELECT * FROM instrument_close ORDER BY instrument_id ASC\",\n        )\n        .fetch_all(pool)\n        .await\n        .map(|rows| rows.into_iter().map(|row| row.0).collect())\n        .map_err(|e| anyhow::anyhow!(\"Failed to load instrument closes: {e}\"))\n    }\n\n    /// Inserts an `OrderInitialized` event via the provided `pool`.","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/infrastructure/src/sql/queries.rs#L253-L289","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: ignores failure mode, keeps going\nadd_instrument_close(&pool, close).await?;\n\n// after: detect duplicate-key errors specifically and retry/skip\nif let Err(e) = add_instrument_close(&pool, close).await {\n    let msg = format!(\"{e:#}\");\n    if msg.contains(\"duplicate key\") {\n        tracing::warn!(\"instrument close already exists, skipping\");\n    } else {\n        return Err(e);\n    }\n}","handlingStrategy":"validation","validationCode":"// Ensure the table exists and this close is not already persisted\nlet exists = sqlx::query_scalar::<_, i64>(\n    \"SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'instrument_close'\")\n    .fetch_one(pool).await? > 0;\nlet dup = exists && sqlx::query_scalar::<_, i64>(\n    \"SELECT COUNT(*) FROM instrument_close WHERE instrument_id = $1 AND ts_init = $2\")\n    .bind(close.instrument_id.to_string())\n    .bind(close.ts_init.to_string())\n    .fetch_one(pool).await? > 0;\nif dup { return Ok(()); }","typeGuard":null,"tryCatchPattern":"if let Err(e) = add_instrument_close(&pool, &close).await {\n    let msg = format!(\"{e:#}\");\n    if msg.contains(\"duplicate key\") { tracing::debug!(\"skipping duplicate close\"); }\n    else { return Err(e); }\n}","preventionTips":["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."],"tags":["database","postgres","sqlx","rust","insert"],"backgroundTag":"database-write-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}