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
- 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).
- 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.
- Fix the source of the bad timestamps (test fixtures using extreme DateTimes, or unsynchronized clocks).
- 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
- Use checked_num_microseconds() instead of num_microseconds().expect() when converting durations for export.
- Reject or clamp spans where end < start (clock skew, VM suspend) before export.
- Avoid extreme placeholder DateTimes (MIN/MAX, year 262000) in trace test fixtures.
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
- timestamp, {}, out of range for precision: {:?}
- cannot fit duration into u64
- not poisoned
- not poisoned
- ghost queue is NOT empty
AI-assisted analysis of influxdata/influxdb@d28e26e048 (2026-08-16).
Data as JSON: /api/errors/4c7027470ef582cb.
Report an issue: GitHub.