nautechsystems/nautilus_trader · error
Lighter GTD expire_time must be at least 5 minutes from now
Error message
Lighter GTD expire_time must be at least 5 minutes from now (plus 1 second transport margin)
What it means
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.
Source
Thrown at crates/adapters/lighter/src/websocket/dispatch.rs:2068
/// - `Gtc` / `Day` / `Gtd` without expiry: `now_ms + ORDER_EXPIRY_DEFAULT_GTC_MS`.
/// The venue rejects `-1` for these TIFs with `21711 invalid expiry`.
pub(crate) fn order_expiry_for(
order_type: OrderType,
tif: &TimeInForce,
expire_time: Option<UnixNanos>,
now_ms: i64,
) -> anyhow::Result<i64> {
if order_type == OrderType::Market {
return Ok(ORDER_EXPIRY_IOC);
}
if matches!(tif, TimeInForce::Gtd)
&& let Some(ts) = expire_time
{
let expiry_ms = (ts.as_u64() / 1_000_000) as i64;
let min_expiry_ms = now_ms.saturating_add(ORDER_EXPIRY_MIN_GTD_MS);
let max_expiry_ms = now_ms.saturating_add(ORDER_EXPIRY_MAX_GTD_MS);
anyhow::ensure!(
expiry_ms >= min_expiry_ms,
"Lighter GTD expire_time must be at least 5 minutes from now (plus 1 second transport margin)",
);
anyhow::ensure!(
expiry_ms <= max_expiry_ms,
"Lighter GTD expire_time must be no more than 30 days from now",
);
return Ok(expiry_ms);
}
if is_conditional_order(order_type) && matches!(tif, TimeInForce::Ioc) {
return Ok(now_ms.saturating_add(ORDER_EXPIRY_DEFAULT_GTC_MS));
}
if matches!(tif, TimeInForce::Ioc | TimeInForce::Fok) {
return Ok(ORDER_EXPIRY_IOC);
}
View on GitHub (pinned to 18893faf8b)
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.
Example fix
// before: short-lived GTD rejected let expire_ns = now_ns + 30_000_000_000; // 30s // after let expire_ns = now_ns + 300_000_000_000; // >= 5 minutes, or use TimeInForce::Ioc
Defensive patterns
Strategy: validation
Validate before calling
let expiry_ms = (expire_ts.as_u64() / 1_000_000) as i64;
let now_ms = current_unix_ms();
if expiry_ms < now_ms + 5 * 60_000 + 1_000 {
// widen expiry or switch to GTC/Ioc before submitting
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Binance Futures goodTillDate must be strictly greater than c
- Binance Spot does not support native GTD; set use_gtd=false
- post-only Derive orders only support GTC time in force; rece
- Lighter has no fill-or-kill TIF; reject FOK at the strategy
- Binance Futures does not support GTD for order type {order_t
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/c89243310da624c0.
Report an issue: GitHub.