{"record":{"id":"c89243310da624c0","repo":"nautechsystems/nautilus_trader","slug":"lighter-gtd-expire-time-must-be-at-least-5-minutes","errorCode":null,"errorMessage":"Lighter GTD expire_time must be at least 5 minutes from now (plus 1 second transport margin)","messagePattern":"Lighter GTD expire_time must be at least 5 minutes from now \\(plus 1 second transport margin\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/lighter/src/websocket/dispatch.rs","lineNumber":2068,"sourceCode":"/// - `Gtc` / `Day` / `Gtd` without expiry: `now_ms + ORDER_EXPIRY_DEFAULT_GTC_MS`.\n///   The venue rejects `-1` for these TIFs with `21711 invalid expiry`.\npub(crate) fn order_expiry_for(\n    order_type: OrderType,\n    tif: &TimeInForce,\n    expire_time: Option<UnixNanos>,\n    now_ms: i64,\n) -> anyhow::Result<i64> {\n    if order_type == OrderType::Market {\n        return Ok(ORDER_EXPIRY_IOC);\n    }\n\n    if matches!(tif, TimeInForce::Gtd)\n        && let Some(ts) = expire_time\n    {\n        let expiry_ms = (ts.as_u64() / 1_000_000) as i64;\n        let min_expiry_ms = now_ms.saturating_add(ORDER_EXPIRY_MIN_GTD_MS);\n        let max_expiry_ms = now_ms.saturating_add(ORDER_EXPIRY_MAX_GTD_MS);\n        anyhow::ensure!(\n            expiry_ms >= min_expiry_ms,\n            \"Lighter GTD expire_time must be at least 5 minutes from now (plus 1 second transport margin)\",\n        );\n        anyhow::ensure!(\n            expiry_ms <= max_expiry_ms,\n            \"Lighter GTD expire_time must be no more than 30 days from now\",\n        );\n        return Ok(expiry_ms);\n    }\n\n    if is_conditional_order(order_type) && matches!(tif, TimeInForce::Ioc) {\n        return Ok(now_ms.saturating_add(ORDER_EXPIRY_DEFAULT_GTC_MS));\n    }\n\n    if matches!(tif, TimeInForce::Ioc | TimeInForce::Fok) {\n        return Ok(ORDER_EXPIRY_IOC);\n    }\n","sourceCodeStart":2050,"sourceCodeEnd":2086,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/websocket/dispatch.rs#L2050-L2086","documentation":"Lighter requires a GTD order's expiry to be at least ORDER_EXPIRY_MIN_GTD_MS (5 minutes plus a 1s transport margin) from submission time; expire_time closer than that is rejected with this error. This prevents orders that would instantly expire or race the venue clock.","triggerScenarios":"Submitting a GTD order whose expire_time (in Unix nanoseconds) converts to a millisecond timestamp earlier than now_ms + ORDER_EXPIRY_MIN_GTD_MS — e.g. expire_time only seconds away, already past, or computed in the wrong unit.","commonSituations":"Passing seconds instead of nanoseconds in expire_time; GTD times computed for short-lived orders (seconds-level TTL); clock skew between the client and venue making a nominally valid expiry too close.","solutions":["Ensure expire_time is nanoseconds since epoch and set GTD expiries at least ~5 minutes in the future.","Use TimeInForce::Gtc (or Ioc/Fok) instead of Gtd for orders intended to live shorter than 5 minutes.","Check unit conversion: ts.as_u64() / 1_000_000 must yield milliseconds; verify the caller isn't passing seconds."],"exampleFix":"// before: short-lived GTD rejected\nlet expire_ns = now_ns + 30_000_000_000; // 30s\n// after\nlet expire_ns = now_ns + 300_000_000_000; // >= 5 minutes, or use TimeInForce::Ioc","handlingStrategy":"validation","validationCode":"let expiry_ms = (expire_ts.as_u64() / 1_000_000) as i64;\nlet now_ms = current_unix_ms();\nif expiry_ms < now_ms + 5 * 60_000 + 1_000 {\n    // widen expiry or switch to GTC/Ioc before submitting\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass expire_time in nanoseconds, not seconds or millis","Set GTD expiries >= 5 minutes in the future","Use Ioc/Gtc for orders intended to live under 5 minutes","Account for clock skew between client and venue"],"tags":["gtd","time-in-force","validation"],"backgroundTag":"value-out-of-range","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"}