influxdata/influxdb · error

unexpected physical type for timestamp column

Error message

unexpected physical type for timestamp column

What it means

derive_min_max_time only handles Statistics::I64 for the timestamp column (IOx stores time as i64 nanoseconds); any other physical type triggers a panic. A non-I64 time column means the file was not produced by IOx's normal write path.

Solutions

  1. Ensure the time column is written as Arrow Timestamp(Nanosecond, None) so it maps to Parquet INT64
  2. Convert non-nanosecond time columns before encoding: cast to TimeValueNanosecond
  3. Extend derive_min_max_time to handle the observed physical type if intentional

Example fix

// before
let time: TimestampMillisecondArray = ...;
// after
let time: TimestampNanosecondArray = time.cast_to(&ArrowDataType::Timestamp(TimeUnit::Nanosecond, None))?;
Defensive patterns

Strategy: validation

Validate before calling

let dt = batch.schema().field_with_name("time")?.data_type();
if *dt != ArrowDataType::Timestamp(TimeUnit::Nanosecond, None) { return Err(anyhow!("time must be ns timestamp")); }

Type guard

fn is_i64_time(dt: &ArrowDataType) -> bool {
    matches!(dt, ArrowDataType::Timestamp(TimeUnit::Nanosecond, None))
}

Prevention

When it happens

Trigger: Calling derive_min_max_time on a Parquet file whose 'time' column physical type is not INT64 (e.g. TimestampMicrosecond stored differently, string, or int32).

Common situations: Writing the time column as Arrow Timestamp with non-nanosecond unit, foreign Parquet files, schema drift after an arrow/parquet upgrade.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19). Data as JSON: /api/errors/af0aceb36110bf6b. Report an issue: GitHub.

Appendix: source

Thrown at core/parquet_file/src/metadata.rs:581

/// metadata.
///
/// [`RecordBatch`]: arrow::record_batch::RecordBatch
pub fn derive_min_max_time(stats: Vec<ColumnSummary>) -> TimestampRange {
    let time_summary = stats
        .into_iter()
        .find(|v| v.name == TIME_COLUMN_NAME)
        .expect("no time column in metadata statistics");

    assert_eq!(time_summary.influxdb_type, InfluxDbType::Timestamp);

    // Extract the min/max timestamps.
    match time_summary.stats {
        Statistics::I64(stats) => {
            let min = Timestamp::new(stats.min.expect("no min time statistic"));
            let max = Timestamp::new(stats.max.expect("no max time statistic"));
            TimestampRange { min, max }
        }
        _ => panic!("unexpected physical type for timestamp column"),
    }
}
/// Parquet metadata with IOx-specific wrapper.
#[derive(Clone, PartialEq, Eq)]
pub struct IoxParquetMetaData {
    /// [Apache Parquet] metadata as freestanding [Apache Thrift]-encoded, and [Zstandard]-compressed bytes.
    ///
    /// This can be used to store metadata separate from the related payload data. The usage of [Apache Thrift] allows the
    /// same stability guarantees as the usage of an ordinary [Apache Parquet] file. To encode a thrift message into bytes
    /// the [Thrift Compact Protocol] is used.
    ///
    /// [Apache Parquet]: https://parquet.apache.org/
    /// [Apache Thrift]: https://thrift.apache.org/
    /// [Thrift Compact Protocol]: https://github.com/apache/thrift/blob/master/doc/specs/thrift-compact-protocol.md
    /// [Zstandard]: http://facebook.github.io/zstd/
    data: Vec<u8>,
}

View on GitHub (pinned to 06200ef96b)