influxdata/influxdb · error

generation duration overflows u64 nanoseconds

Error message

generation duration overflows u64 nanoseconds

What it means

A `.expect(...)` panic in the v2->v3 migration when converting a v2 generation duration (a `Duration`) to `u64` nanoseconds via `u64::try_from(duration.as_nanos())`. It fires only if a v2 catalog stores a generation duration of 256+ years (`as_nanos()` exceeds `u64::MAX`), which the v3 `SetGenerationDuration` record cannot represent.

Solutions

  1. Locate the offending generation duration in the v2 catalog snapshot and correct it to a realistic value (e.g. seconds/hours).
  2. Restore the v2 catalog object from a backup taken before the bad value was written.
  3. If you control the source, replace `expect` with a fallible conversion and emit `MigrationError::Unexpected` instead of panicking.
  4. Report to InfluxData if legitimate v2 data triggers this — it indicates the migration lacks graceful overflow handling.

Example fix

// before
.expect("generation duration overflows u64 nanoseconds")
// after
.duration_ns(u64::try_from(duration.as_nanos()).map_err(|_| {
    MigrationError::Unexpected(anyhow::anyhow!("generation duration {duration:?} overflows u64 ns"))
})?)
Defensive patterns

Strategy: validation

Validate before calling

fn generation_duration_is_representable(d: std::time::Duration) -> bool {
    u64::try_from(d.as_nanos()).is_ok()
}

Type guard

fn fits_u64_nanos(v: u128) -> bool { v <= u64::MAX as u128 }

Try / catch

// panic, not recoverable at runtime; pre-validate any duration you write to a v2 catalog
assert!(fits_u64_nanos(d.as_nanos()), "generation duration too large for v3 migration");

Prevention

When it happens

Trigger: Running `check_and_migrate_v2_to_v3` over a v2 catalog whose `generation_durations` map contains a duration whose nanosecond count does not fit in u64 (>= ~584.9 years of nanoseconds... specifically > u64::MAX ns, i.e. >= 2^64 ns).

Common situations: Hand-modified or corrupted v2 catalog snapshots with absurd generation durations; test fixtures using `Duration::MAX`; catalogs written by tooling that didn't bound-check duration values.

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 influxdata/influxdb@06200ef96b (2026-09-19). Data as JSON: /api/errors/b56f8c38fb39cabc. Report an issue: GitHub.

Appendix: source

Thrown at influxdb3_catalog/src/catalog/migrations/v3.rs:240

    snapshot_next: u64,
    derived_next: u64,
    batch: &mut RecordBatch,
) {
    if snapshot_next > derived_next {
        batch.push(&SetNextId {
            scope,
            id: snapshot_next,
        });
    }
}

impl FromV2 for GenerationConfigSnapshot {
    fn from_v2(&self, batch: &mut RecordBatch) {
        for (level, duration) in &self.generation_durations {
            batch.push(&SetGenerationDuration {
                level: *level,
                duration_ns: u64::try_from(duration.as_nanos())
                    .expect("generation duration overflows u64 nanoseconds"),
            });
        }
    }
}

impl FromV2 for NodeSnapshot {
    /// `RegisterNode` is always emitted. For nodes whose state is Stopped, v2
    /// doesn't preserve the original registration timestamp — use 0 as the
    /// documented placeholder, then emit `StopNode` to drive the node into
    /// the Stopped state on apply.
    fn from_v2(&self, batch: &mut RecordBatch) {
        let registered_time_ns = match &self.state {
            NodeStateSnapshot::Running { registered_time_ns } => *registered_time_ns,
            NodeStateSnapshot::Stopped { .. } => 0,
        };

        batch.push(&RegisterNode {
            node_catalog_id: self.node_catalog_id.get(),

View on GitHub (pinned to 06200ef96b)