nautechsystems/nautilus_trader · error

event_slug_builder offset resolves before the Unix epoch

Error message

event_slug_builder offset resolves before the Unix epoch

What it means

Slug timestamps are computed as period_start + (start_offset_periods + period) * period_secs using i128 arithmetic; if the result is negative, the computed period starts before the Unix epoch and no valid Polymarket slug exists, so the builder bails.

Source

Thrown at crates/adapters/polymarket/src/config.rs:138

        let assets = self.normalized_assets();
        if assets.is_empty() {
            anyhow::bail!("event_slug_builder.assets must include at least one non-empty asset");
        }

        let period_secs = self
            .interval_mins
            .checked_mul(60)
            .ok_or_else(|| anyhow::anyhow!("event_slug_builder.interval_mins is too large"))?;
        let period_start = (unix_secs / period_secs) * period_secs;
        let period_secs = i128::from(period_secs);
        let period_start = i128::from(period_start);
        let mut slugs = Vec::new();

        for period in 0..self.periods {
            let period_offset = i128::from(self.start_offset_periods) + i128::from(period);
            let timestamp = period_start + period_offset * period_secs;
            if timestamp < 0 {
                anyhow::bail!("event_slug_builder offset resolves before the Unix epoch");
            }

            for asset in &assets {
                slugs.push(format!(
                    "{asset}-updown-{}m-{timestamp}",
                    self.interval_mins
                ));
            }
        }

        Ok(slugs)
    }

    fn normalized_assets(&self) -> Vec<String> {
        let mut assets = Vec::new();

        for asset in &self.assets {
            let asset = asset.trim().to_ascii_lowercase();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set start_offset_periods to a non-negative (or only slightly negative) value so period_start + offset*period_secs >= 0
  2. Clamp or validate start_offset_periods in config parsing against period_secs
  3. If using a fixed test clock, ensure the base timestamp is comfortably after the epoch

Example fix

// before
start_offset_periods = -10_000_000_000 // underflows below epoch
// after
start_offset_periods = -1 // only look back one period
Defensive patterns

Strategy: validation

Validate before calling

let period_secs = interval_mins * 60;
let first = i128::from(now) + i128::from(start_offset_periods) * i128::from(period_secs);
assert!(first >= 0, "offset resolves before Unix epoch");

Try / catch

match builder.build_event_slugs() {
    Ok(slugs) => use(slugs),
    Err(e) => { log::error!("slug offset invalid: {e}"); return Err(e); }
}

Prevention

When it happens

Trigger: A large negative start_offset_periods combined with a small now/period_start pushes the first period timestamp below 0 (only possible with synthetic/test clocks or extreme negative offsets, since period_start is typically current Unix time).

Common situations: Test harnesses with unix_secs near 0; config carrying a very negative start_offset_periods after a mistaken unit conversion.

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/a259c75379152d43. Report an issue: GitHub.