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
- Ensure the time column is written as Arrow Timestamp(Nanosecond, None) so it maps to Parquet INT64
- Convert non-nanosecond time columns before encoding: cast to TimeValueNanosecond
- 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
- Write time as Timestamp(Nanosecond, None) so Parquet stores INT64
- Cast time columns to nanoseconds before encoding
- Add a unit test asserting the encoded physical type is INT64
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
- Could not convert Parquet schema to Arrow schema
- expected bucket id, got string
- expected bucket id, got TS range
- expected datatime column value but got
- expected i64 got
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)