influxdata/influxdb · error

no overflow

Error message

no overflow

What it means

When exporting a finished span to Jaeger, the code converts the span duration with (end - start).num_microseconds().expect("no overflow"). chrono's num_microseconds returns None when the Duration in microseconds does not fit in i64 — roughly |duration| > 292,000 years — so the panic means the span's end timestamp is absurdly far from its start. Realistically this comes from corrupt timestamps or a clock that jumped (end long before start / far future), not from a genuinely long trace.

Source

Thrown at core/trace_exporters/src/jaeger/span.rs:30

    let trace_id_low = trace_id as i64;
    (trace_id_high, trace_id_low)
}

impl TryFrom<Span> for jaeger::Span {
    type Error = String;

    fn try_from(mut s: Span) -> Result<Self, Self::Error> {
        let (trace_id_high, trace_id_low) = split_trace_id(s.ctx.trace_id);

        // A parent span id of 0 indicates no parent span ID (span IDs are non-zero)
        let parent_span_id = s.ctx.parent_span_id.map(|id| id.get()).unwrap_or_default() as i64;

        let (start_time, duration) = match (s.start, s.end) {
            (Some(start), Some(end)) => (
                start.timestamp_nanos_opt().ok_or_else(|| {
                    format!("start timestamp cannot be represented as nanos: {start}")
                })? / 1000,
                (end - start).num_microseconds().expect("no overflow"),
            ),
            (Some(start), _) => (
                start.timestamp_nanos_opt().ok_or_else(|| {
                    format!("start timestamp cannot be represented as nanos: {start}")
                })? / 1000,
                0,
            ),
            _ => (0, 0),
        };

        // These don't appear to be standardised, however, the jaeger UI treats
        // the presence of an "error" tag as indicating an error
        match s.status {
            SpanStatus::Ok => {
                s.metadata
                    .entry("ok".into())
                    .or_insert(MetaValue::Bool(true));
            }

View on GitHub (pinned to d28e26e048)

Solutions

  1. Validate span timestamps before export: require end >= start and reject spans whose duration cannot be represented (mirror the timestamp_nanos_opt error handling already used for start).
  2. Use checked_num_microseconds() and map None to a proper error: .and_then(|d| d.checked_num_microseconds()).ok_or_else(|| format!(...))? — this turns the panic into an Err like the start-time branch.
  3. Fix the source of the bad timestamps (test fixtures using extreme DateTimes, or unsynchronized clocks).
  4. Clamp the duration (e.g. saturating at i64::MAX µs) if dropping the span is worse than exporting a truncated duration.

Example fix

// before (core/trace_exporters/src/jaeger/span.rs)
let (start_time, duration) = match (s.start, s.end) {
    (Some(start), Some(end)) => (
        start.timestamp_nanos_opt().ok_or_else(|| ...)? / 1000,
        (end - start).num_microseconds().expect("no overflow"),
    ),
    ...
};

// after: return an error like the start branch does
(Some(start), Some(end)) => {
    let duration = (end - start)
        .num_microseconds()
        .ok_or_else(|| format!("duration cannot be represented as micros: {}", end - start))?;
    (
        start.timestamp_nanos_opt().ok_or_else(|| ...)? / 1000,
        duration,
    )
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate before converting a Span to the Jaeger thrift type:
fn span_duration_micros(s: &Span) -> Option<i64> {
    match (s.start, s.end) {
        (Some(start), Some(end)) => {
            if end < start { return None; }
            (end - start).num_microseconds()
        }
        _ => Some(0),
    }
}

match span_duration_micros(&span) {
    Some(us) => export(span, us),
    None => warn!("dropping span with unrepresentable duration"),
}

Type guard

fn has_representable_duration(s: &Span) -> bool {
    match (s.start, s.end) {
        (Some(a), Some(b)) => b >= a && (b - a).num_microseconds().is_some(),
        _ => true,
    }
}

Prevention

When it happens

Trigger: Exporting a Span to the Jaeger thrift format where s.end - s.end-start exceeds i64 microseconds: a negative duration from end < start combined with extreme values, timestamps near chrono::DateTime min/max (year ±262000), or clocks adjusted via NTP/VM migration between start and end capture. The sibling start path uses timestamp_nanos_opt().ok_or_else(...) and returns an error properly; only the duration path panics.

Common situations: Test code constructing spans with placeholder timestamps (DateTime::<Utc>::MIN/MAX or UNIX_EPOCH defaults); VM suspend/resume or clock skew producing a wildly wrong end time; ingesting trace data from another system with corrupt epoch values; fuzzing the exporter with arbitrary DateTimes.

Related errors


AI-assisted analysis of influxdata/influxdb@d28e26e048 (2026-08-16). Data as JSON: /api/errors/4c7027470ef582cb. Report an issue: GitHub.