{"record":{"id":"7fda81d433c7d8c9","repo":"nautechsystems/nautilus_trader","slug":"provider-order-time-in-force-does-not-match-cac","errorCode":null,"errorMessage":"provider order time in force {} does not match cached order time in force {}","messagePattern":"provider order time in force (.+?) does not match cached order time in force (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/execution/reconciliation.rs","lineNumber":493,"sourceCode":"        provider_order.original_size,\n        expected_quantity,\n    );\n    Ok(())\n}\n\nfn validate_client_bound_order_row(\n    provider_order: &PolymarketOpenOrder,\n    cached_order: &OrderAny,\n    expected_quantity: Quantity,\n    provider_expire_time: Option<UnixNanos>,\n) -> anyhow::Result<()> {\n    anyhow::ensure!(\n        cached_order.order_side() == provider_order.side.into(),\n        \"provider order side {} does not match cached order side {}\",\n        provider_order.side,\n        cached_order.order_side(),\n    );\n    anyhow::ensure!(\n        cached_order.time_in_force() == provider_order.order_type.into(),\n        \"provider order time in force {} does not match cached order time in force {}\",\n        provider_order.order_type,\n        cached_order.time_in_force(),\n    );\n    validate_client_bound_order_quantity(provider_order, expected_quantity)?;\n    let cached_price = cached_order\n        .price()\n        .context(\"cached Limit order is missing price\")?;\n    anyhow::ensure!(\n        cached_price.as_decimal() == provider_order.price,\n        \"provider order price {} does not match cached order price {cached_price}\",\n        provider_order.price,\n    );\n\n    let provider_expire_seconds = provider_expire_time.map(|value| value.as_seconds());\n    let cached_expire_seconds = cached_order\n        .expire_time()","sourceCodeStart":475,"sourceCodeEnd":511,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/execution/reconciliation.rs#L475-L511","documentation":"The last cross-check in validate_client_bound_order_row compares the cached order's time_in_force with the provider order_type (converted via TimeInForce::From). If the venue-side order type (e.g. GTC, GTD, FOK, FAK) differs from what the client cached, this anyhow error fires, since reconciling against a differently-timed order would corrupt order-state tracking.","triggerScenarios":"build_order_report_from_order -> validate_client_bound_order_row encounters a provider order_type that maps to a different TimeInForce than cached_order.time_in_force() — e.g. the order was amended on-venue to a different type, or the local cache recorded Gtc for an order submitted as Gtd/Fok.","commonSituations":"Provider amended the order's expiration/type after submission while the cache kept the original TIF; adapter version upgrade changing the order_type -> TimeInForce mapping (e.g. FOK vs FAK semantics); strategy submitted a GTD order but the cache defaulted to GTC; provider reclassified an expired GTD order as GTC.","solutions":["Refresh the cached order's time_in_force from the provider's authoritative order_type and retry reconciliation.","Ensure the TIF recorded in the SubmitOrder command is what is stored in the cache (no defaulting to Gtc for GTD orders).","Check the provider order_type -> TimeInForce conversion in the adapter for a regression after a Polymarket API/SDK update.","If the order was legitimately amended on-venue, process the provider modify/update event so the cache reflects the new TIF before building the report."],"exampleFix":"// before\nlet mut cached = build_order(req)?; // req.tif = Gtd\ncached.set_time_in_force(TimeInForce::Gtc); // accidental default\nlet report = build_order_report_from_order(&provider_order, &cached)?;\n// after\ncached.set_time_in_force(TimeInForce::from(req.order_type)); // Gtd matches provider\nlet report = build_order_report_from_order(&provider_order, &cached)?;","handlingStrategy":"validation","validationCode":"fn tif_matches(provider: &PolymarketOpenOrder, cached: &OrderAny) -> bool {\n    cached.time_in_force() == provider.order_type.into()\n}\n// verify at submission time:\nanyhow::ensure!(TimeInForce::from(submit_cmd.order_type) == submit_cmd.time_in_force, \"TIF mismatch\");","typeGuard":"fn tif_consistent(p: &PolymarketOpenOrder, c: &OrderAny) -> bool {\n    c.time_in_force() == p.order_type.into()\n}","tryCatchPattern":"match build_order_report_from_order(&provider_order, &cached) {\n    Err(e) if e.to_string().contains(\"does not match cached order time in force\") => {\n        refresh_cached_tif_from_provider(&mut cached, provider_order.order_type); // resync then retry\n    }\n    other => other?,\n}","preventionTips":["Never default cached time_in_force to Gtc — always derive it from the submitted order_type","Apply provider amend/update events to the cache before reconciliation","Add regression tests for the order_type -> TimeInForce mapping when the adapter or provider API changes"],"tags":["polymarket","reconciliation","order-state","time-in-force"],"backgroundTag":"internal-invariant-violation","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"}