nautechsystems/nautilus_trader · error · anyhow::Error
Binance Futures goodTillDate must be smaller than {BINANCE_G
Error message
Binance Futures goodTillDate must be smaller than {BINANCE_GTD_MAX_MILLIS} What it means
Binance caps goodTillDate below the maximum representable exchange timestamp: the millisecond value must stay under BINANCE_GTD_MAX_MILLIS = 253_402_300_799_000 (execution.rs:141), i.e. before 9999-12-31T23:59:59Z. The client converts the whole-second expiry to milliseconds and rejects anything at or beyond the cap before sending, avoiding a venue-side parse failure.
Source
Thrown at crates/adapters/binance/src/futures/execution.rs:1203
let expire_time = expire_time.context("Binance Futures GTD requires an expire_time")?;
let expire_ns = expire_time.as_u64();
anyhow::ensure!(
expire_ns.is_multiple_of(NANOSECONDS_IN_SECOND),
"Binance Futures goodTillDate requires whole-second precision"
);
let minimum_ns = ts_now
.as_u64()
.checked_add(BINANCE_GTD_MIN_LEAD_SECS * NANOSECONDS_IN_SECOND)
.context("Binance Futures GTD minimum timestamp overflow")?;
anyhow::ensure!(
expire_ns > minimum_ns,
"Binance Futures goodTillDate must be strictly greater than current time plus {BINANCE_GTD_MIN_LEAD_SECS} seconds"
);
let good_till_date = expire_ns / NANOSECONDS_IN_MILLISECOND;
anyhow::ensure!(
good_till_date < BINANCE_GTD_MAX_MILLIS,
"Binance Futures goodTillDate must be smaller than {BINANCE_GTD_MAX_MILLIS}"
);
Ok(FuturesOrderLifetime {
time_in_force,
good_till_date: Some(i64::try_from(good_till_date)?),
})
}
fn is_grouped_order(order: &OrderAny) -> bool {
matches!(
order.contingency_type(),
Some(contingency_type) if contingency_type != ContingencyType::NoContingency
) || order
.linked_order_ids()
.is_some_and(|linked_order_ids| !linked_order_ids.is_empty())
}View on GitHub (pinned to a4b06ed870)
Solutions
- Use a realistic expiry date (same day/session horizon) instead of a far-future sentinel
- For 'no expiry', submit GTC rather than a maximal goodTillDate
- Verify the expire_time is in nanoseconds and within ordinary date ranges before submission
Example fix
// before let expire = UnixNanos::from(4_102_444_800_000 * 1_000_000_000); // ms value treated as seconds -> year ~130000 // after let expire = UnixNanos::from(1_767_225_600 * 1_000_000_000); // 2026-01-01T00:00:00Z in ns
Defensive patterns
Strategy: validation
Validate before calling
const MAX_GTD_MS: u64 = 253_402_300_799_000; // 9999-12-31T23:59:59Z let gtd_ms = expire.as_u64() / 1_000_000; anyhow::ensure!(gtd_ms < MAX_GTD_MS, "goodTillDate beyond exchange maximum");
Type guard
fn within_gtd_max(expire_ns: u64) -> bool {
expire_ns / 1_000_000 < 253_402_300_799_000
} Prevention
- Never use max-int sentinels for expiry; use GTC for no-expiry orders
- Double-check unit (ns vs ms/us) whenever constructing UnixNanos from foreign timestamps
- Bound expiry to a sane horizon (same day/week) in config validation
When it happens
Trigger: Submitting a GTD order whose expire_time corresponds to a year >= 10000 — e.g. an expiry accidentally built in microseconds/milliseconds instead of nanoseconds (making it astronomically large), or a sentinel 'far future' value like u64::MAX/i64::MAX passed as the expiry.
Common situations: Unit confusion when constructing UnixNanos from ms/us values; 'never expire' sentinels expressed as max integers instead of GTC; date libraries defaulting to far-future dates on parse errors.
Related errors
- Binance Futures goodTillDate requires whole-second precision
- Binance Futures goodTillDate must be strictly greater than c
- Binance Futures does not support GTD for order type {order_t
- Binance Futures GTD cannot be post-only
- Binance {product_type:?} Futures does not support native GTD
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/f6a04049ab584595.
Report an issue: GitHub.