pola-rs/polars · error

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

Error message

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

What it means

The Duration branch of the JSON serializer has converters only for Nanosecond/Microsecond/Millisecond; Duration(TimeUnit::Second) - legal in Arrow - hits the catch-all panic. Second-precision durations typically enter via arrow-rs interop or hand-constructed arrays.

Source

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

                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,
                TimeUnit::Microsecond => duration_us_to_duration,
                TimeUnit::Millisecond => duration_ms_to_duration,
                tu => panic!("Invalid time unit '{tu:?}' for Duration."),
            };
            duration_serializer(
                array.as_any().downcast_ref().unwrap(),
                convert,
                offset,
                take,
            )
        },
        ArrowDataType::Time64(tu) => {
            let convert = match tu {
                TimeUnit::Nanosecond => time64ns_to_time,
                tu => panic!("Invalid time unit '{tu:?}' for Time."),
            };
            time_serializer(
                array.as_any().downcast_ref().unwrap(),
                convert,
                offset,
                take,

View on GitHub (pinned to 9b5d73fd00)

Solutions

  1. Cast to Duration(Milliseconds) (or us/ns) before write_json
  2. Or store as Int64 seconds and format units yourself
  3. Validate the dtype list before exporting JSON

Example fix

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

// after
let df = df.with_column(col("d").cast(&DataType::Duration(TimeUnit::Milliseconds)))?;
df.write_json(&mut out)?;
Defensive patterns

Strategy: fallback

Validate before calling

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

Type guard

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

Try / catch

catch_unwind around the write to convert the panic into a clean export error; casting Duration(Second) -> Duration(Milliseconds) beforehand is the durable fix.

Prevention

When it happens

Trigger: write_json on a column typed ArrowDataType::Duration(TimeUnit::Second), e.g. an arrow-rs array built with DurationSecondType or converted from ISO-8601 'PT5S' values.

Common situations: arrow-rs interop; pipelines that normalized durations to seconds for storage.

Related errors


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