pola-rs/polars · error

Invalid time unit '{tu:?}' for Datetime.

Error message

Invalid time unit '{tu:?}' for Datetime.

What it means

The JSON writer maps naive Timestamp columns to chrono converters only for Nanosecond, Microsecond and Millisecond units. Timestamp(TimeUnit::Second, None) - legal in Arrow and typical of arrow-rs interop - falls through to the catch-all panic. Second-with-tz goes down timestamp_tz_serializer instead, so this is specifically the naive path.

Source

Thrown at crates/polars-json/src/json/write/serialize.rs:591

                dictionary_utf8view_serializer::<u32>(array, offset, take)
            },
            _ => {
                // Not produced by polars
                unreachable!()
            },
        },
        ArrowDataType::Date32 => date_serializer(
            array.as_any().downcast_ref().unwrap(),
            date32_to_date,
            offset,
            take,
        ),
        ArrowDataType::Timestamp(tu, None) => {
            let convert = match tu {
                TimeUnit::Nanosecond => timestamp_ns_to_datetime,
                TimeUnit::Microsecond => timestamp_us_to_datetime,
                TimeUnit::Millisecond => timestamp_ms_to_datetime,
                tu => panic!("Invalid time unit '{tu:?}' for Datetime."),
            };
            timestamp_serializer(
                array.as_any().downcast_ref().unwrap(),
                convert,
                offset,
                take,
            )
        },
        ArrowDataType::Timestamp(time_unit, Some(tz)) => timestamp_tz_serializer(
            array.as_any().downcast_ref().unwrap(),
            *time_unit,
            tz,
            offset,
            take,
        ),
        ArrowDataType::Duration(tu) => {
            let convert = match tu {
                TimeUnit::Nanosecond => duration_ns_to_duration,

View on GitHub (pinned to 9b5d73fd00)

Solutions

  1. Cast to a supported unit before writing: df.cast / with_column(col("ts").cast(&DataType::Datetime(TimeUnit::Microseconds, None)))
  2. If building arrays yourself, encode timestamps in us/ms/ns rather than s
  3. Inspect dtypes (schema) before write_json and normalize units

Example fix

// before: column typed Timestamp(Second, None)
df.write_json(&mut out)?; // panic: Invalid time unit 'Second' for Datetime.

// after
let df = df.with_column(col("ts").cast(&DataType::Datetime(TimeUnit::Microseconds, None)))?;
df.write_json(&mut out)?;
Defensive patterns

Strategy: fallback

Validate before calling

fn normalize_timestamp_units(df: &DataFrame) -> PolarsResult<DataFrame> {
    let mut exprs = vec![];
    for (name, dt) in df.schema().iter() {
        if matches!(dt, DataType::Datetime(TimeUnit::Seconds, _)) {
            exprs.push(col(name).cast(&DataType::Datetime(TimeUnit::Microseconds, dt.time_zone().cloned())).alias(name));
        }
    }
    df.lazy().with_columns(exprs).collect()
}
// run before write_json / JsonWriter::write

Type guard

fn has_writable_timestamp_units(df: &DataFrame) -> bool {
    df.schema().iter_values().all(|dt| match dt {
        DataType::Datetime(tu, _) => !matches!(tu, TimeUnit::Seconds),
        _ => true,
    })
}

Try / catch

catch_unwind(AssertUnwindSafe(|| df.write_json(&mut out))) to get a job-level error; casting Seconds -> Microseconds beforehand removes the failing branch entirely.

Prevention

When it happens

Trigger: write_json on an Int64 array typed ArrowDataType::Timestamp(TimeUnit::Second, None): data converted from arrow-rs RecordBatches, IPC/Avro readers, or hand-built arrays using second-precision epoch values.

Common situations: arrow-rs/polars interop boundaries; datasets produced by systems that store epoch-seconds.

Related errors


AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19). Data as JSON: /api/errors/23c3bb98bea4ff48. Report an issue: GitHub.