nautechsystems/nautilus_trader · error
Lighter GTD expire_time must be no more than 30 days from no
Error message
Lighter GTD expire_time must be no more than 30 days from now
What it means
Lighter imposes venue-side limits on GTD (Good-Til-Date) order expiry timestamps. The adapter validates that expire_time is at least 5 minutes from now (plus a 1s transport margin) and at most 30 days out. This error fires when expiry_ms exceeds the 30-day maximum computed from current time.
Source
Thrown at crates/adapters/lighter/src/websocket/dispatch.rs:2072
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);
}
Ok(now_ms.saturating_add(ORDER_EXPIRY_DEFAULT_GTC_MS))
}
fn is_conditional_market_order(order_type: OrderType) -> bool {View on GitHub (pinned to 18893faf8b)
Solutions
- Reduce the GTD expire_time to 30 days from now or less
- If a longer horizon is needed, use a non-GTD time-in-force (e.g. GTC) or re-issue/extend the order periodically within 30 days
- Check any expire-time configuration for ms-vs-s or other unit errors inflating the value
Example fix
// before let expire_time = now_ms + 60 * 24 * 3600 * 1000; // 60 days // after let expire_time = now_ms + 30 * 24 * 3600 * 1000; // 30 days (venue max)
Defensive patterns
Strategy: validation
Validate before calling
let max_expiry = now_ms + ORDER_EXPIRY_MAX_GTD_MS; let min_expiry = now_ms + ORDER_EXPIRY_MIN_GTD_MS; anyhow::ensure!(expiry_ms >= min_expiry && expiry_ms <= max_expiry, "GTD expiry out of venue range");
Type guard
fn is_valid_gtd_expiry(expiry_ms: i64, now_ms: i64) -> bool {
expiry_ms >= now_ms + ORDER_EXPIRY_MIN_GTD_MS && expiry_ms <= now_ms + ORDER_EXPIRY_MAX_GTD_MS
} Try / catch
match client.submit_order(order) {
Err(e) if e.to_string().contains("no more than 30 days") => {
// clamp expiry to 30 days and resubmit
}
other => other?,
} Prevention
- Clamp GTD expiry to now+30d at order construction time
- Prefer GTC for long-horizon orders
- Unit-test expiry computation for ms/second confusion
When it happens
Trigger: Submitting or modifying a GTD order whose expire_time is more than ORDER_EXPIRY_MAX_GTD_MS (30 days) in the future, e.g. a strategy passing a 60-day expiry or a hardcoded far-future timestamp.
Common situations: Configured order TTL longer than the venue permits; default strategy time_in_expiry values ported from another adapter (e.g. Binance allows longer); unit confusion (ms vs seconds) inflating the timestamp.
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
- OrderList denied: {e}
- Unsupported `OrderSide` for Binance: {value:?}
- Invalid order side: {e}
- Binance Futures goodTillDate must be smaller than {BINANCE_G
- {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/32b1e6ed8fa80729.
Report an issue: GitHub.