{"record":{"id":"4c7027470ef582cb","repo":"influxdata/influxdb","slug":"no-overflow","errorCode":null,"errorMessage":"no overflow","messagePattern":"no overflow","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/trace_exporters/src/jaeger/span.rs","lineNumber":30,"sourceCode":"    let trace_id_low = trace_id as i64;\n    (trace_id_high, trace_id_low)\n}\n\nimpl TryFrom<Span> for jaeger::Span {\n    type Error = String;\n\n    fn try_from(mut s: Span) -> Result<Self, Self::Error> {\n        let (trace_id_high, trace_id_low) = split_trace_id(s.ctx.trace_id);\n\n        // A parent span id of 0 indicates no parent span ID (span IDs are non-zero)\n        let parent_span_id = s.ctx.parent_span_id.map(|id| id.get()).unwrap_or_default() as i64;\n\n        let (start_time, duration) = match (s.start, s.end) {\n            (Some(start), Some(end)) => (\n                start.timestamp_nanos_opt().ok_or_else(|| {\n                    format!(\"start timestamp cannot be represented as nanos: {start}\")\n                })? / 1000,\n                (end - start).num_microseconds().expect(\"no overflow\"),\n            ),\n            (Some(start), _) => (\n                start.timestamp_nanos_opt().ok_or_else(|| {\n                    format!(\"start timestamp cannot be represented as nanos: {start}\")\n                })? / 1000,\n                0,\n            ),\n            _ => (0, 0),\n        };\n\n        // These don't appear to be standardised, however, the jaeger UI treats\n        // the presence of an \"error\" tag as indicating an error\n        match s.status {\n            SpanStatus::Ok => {\n                s.metadata\n                    .entry(\"ok\".into())\n                    .or_insert(MetaValue::Bool(true));\n            }","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/influxdata/influxdb/blob/d28e26e048401c53cbb98cf2d6ab0cf1e98048ca/core/trace_exporters/src/jaeger/span.rs#L12-L48","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (core/trace_exporters/src/jaeger/span.rs)\nlet (start_time, duration) = match (s.start, s.end) {\n    (Some(start), Some(end)) => (\n        start.timestamp_nanos_opt().ok_or_else(|| ...)? / 1000,\n        (end - start).num_microseconds().expect(\"no overflow\"),\n    ),\n    ...\n};\n\n// after: return an error like the start branch does\n(Some(start), Some(end)) => {\n    let duration = (end - start)\n        .num_microseconds()\n        .ok_or_else(|| format!(\"duration cannot be represented as micros: {}\", end - start))?;\n    (\n        start.timestamp_nanos_opt().ok_or_else(|| ...)? / 1000,\n        duration,\n    )\n}","handlingStrategy":"validation","validationCode":"// Validate before converting a Span to the Jaeger thrift type:\nfn span_duration_micros(s: &Span) -> Option<i64> {\n    match (s.start, s.end) {\n        (Some(start), Some(end)) => {\n            if end < start { return None; }\n            (end - start).num_microseconds()\n        }\n        _ => Some(0),\n    }\n}\n\nmatch span_duration_micros(&span) {\n    Some(us) => export(span, us),\n    None => warn!(\"dropping span with unrepresentable duration\"),\n}","typeGuard":"fn has_representable_duration(s: &Span) -> bool {\n    match (s.start, s.end) {\n        (Some(a), Some(b)) => b >= a && (b - a).num_microseconds().is_some(),\n        _ => true,\n    }\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["rust","jaeger","tracing","chrono","timestamp","overflow","panic"],"backgroundTag":"timestamp-overflow","analyzedSha":"d28e26e048401c53cbb98cf2d6ab0cf1e98048ca","analyzedAt":"2026-08-16T19:53:34.623Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}