nautechsystems/nautilus_trader · error · anyhow::Error
fill {} maps to position {position_id} with a different acco
Error message
fill {} maps to position {position_id} with a different account or instrument What it means
The cached position resolved for this fill has an account_id or instrument_id that differs from the fill report's. A fill can only be applied to a position on the same account and instrument, so the manager rejects the report as misattributed.
Source
Thrown at crates/live/src/execution/manager.rs:2892
if !hedge_context {
return Ok(PositionFillReportPreparation::Ready);
}
if report.venue_position_id.is_some() {
return Ok(PositionFillReportPreparation::Ready);
}
let Some(position_id) = mapped_position_id else {
return Ok(PositionFillReportPreparation::Unattributed);
};
let position = cache.position(&position_id).ok_or_else(|| {
anyhow::anyhow!(
"fill {} maps to position {position_id}, which is not cached",
report.trade_id,
)
})?;
anyhow::ensure!(
position.account_id == report.account_id
&& position.instrument_id == report.instrument_id,
"fill {} maps to position {position_id} with a different account or instrument",
report.trade_id,
);
anyhow::ensure!(
position.is_open(),
"fill {} maps to non-open position {position_id}",
report.trade_id,
);
anyhow::ensure!(
!position.is_opposite_side(report.order_side) || report.last_qty <= position.quantity,
"fill {} without a venue position ID would cross position {position_id}",
report.trade_id,
);
report.venue_position_id = Some(position_id);
Ok(PositionFillReportPreparation::Ready)View on GitHub (pinned to 18893faf8b)
Solutions
- Scope adapter-generated position IDs by instrument (and account) so they cannot collide
- Fix instrument symbology mapping so the same venue position resolves to one nautilus instrument
- Verify account_id derivation in the adapter matches the account that owns the position
- Clear corrupt cache mappings and re-reconcile with venue position state
Example fix
// before (global counter)
let position_id = VenuePositionId::new(format!("{n}"));
// after (scoped)
let position_id = VenuePositionId::new(format!("{instrument}-{account}-{n}")); Defensive patterns
Strategy: validation
Validate before calling
if let Some(pos) = cache.position(&position_id) {
assert_eq!(pos.account_id, report.account_id);
assert_eq!(pos.instrument_id, report.instrument_id);
} Type guard
fn position_matches_report(pos: &Position, report: &TradeReport) -> bool {
pos.account_id == report.account_id && pos.instrument_id == report.instrument_id
} Prevention
- Scope generated position IDs by instrument and account
- Keep one authoritative instrument symbology mapping
- Verify account routing for multi-account deployments
When it happens
Trigger: cache.position(position_id) returns a position whose account_id != report.account_id or instrument_id != report.instrument_id. Caused by position ID collisions in a custom adapter's mapping, wrong instrument symbology, or multi-account routing errors.
Common situations: Adapters that generate position IDs without instrument/account scoping so IDs collide across instruments; an instrument mapping change mid-session; fills from a sub-account routed to a parent-account position.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- fill {} position ID {venue_position_id} conflicts with cache
- fill {} client order ID {report_client_order_id} conflicts w
- fill {} order side or venue order ID differs from cached ord
- fill {} maps to position {position_id}, which is not cached
- fill {} maps to non-open position {position_id}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b0cc0146ebf8e0e1.
Report an issue: GitHub.