pola-rs/polars · error · NotImplementedError

sink to Iceberg table with partition field '{field.name}' on

Error message

sink to Iceberg table with partition field '{field.name}' on source column '{source_name}' with '{metrics_mode}' metrics; partition value inference requires lower and upper bounds

What it means

The Iceberg sink derives partition values from Parquet lower/upper bound statistics. This error means the partition field's source column was written with a metrics mode (e.g. `none` or truncated metrics) that omits the lower/upper bounds needed to infer the partition value. It is raised as NotImplementedError because the sink does not support that configuration combination.

Source

Thrown at py-polars/src/polars/io/iceberg/_sink.py:290

    exprs: list[pl.Expr] = []

    for field in spec.fields:
        source_field = schema.find_field(field.source_id)
        statistics = statistics_plan.get(field.source_id)
        if field.source_id not in nested_source_ids and (
            statistics is None or statistics.mode.type not in bounds_metrics_modes
        ):
            source_name = schema.find_column_name(field.source_id)
            metrics_mode = (
                statistics.mode.type.value if statistics is not None else "unavailable"
            )
            msg = (
                "sink to Iceberg table with partition field "
                f"'{field.name}' on source column '{source_name}' with "
                f"'{metrics_mode}' metrics; partition value inference requires "
                "lower and upper bounds"
            )
            raise NotImplementedError(msg)

        source_type = source_field.field_type
        transform = field.transform
        expr = _partition_source_expr(schema, field.source_id)

        if isinstance(transform, IdentityTransform):
            pass
        elif isinstance(
            transform, (YearTransform, MonthTransform, DayTransform, HourTransform)
        ):
            if type(source_type).__name__ in {
                "TimestamptzType",
                "TimestamptzNanoType",
            }:
                expr = expr.dt.convert_time_zone("UTC")

            if isinstance(transform, YearTransform):
                expr = expr.dt.year() - 1970

View on GitHub (pinned to fc24390824)

Solutions

  1. Change the table property so the partition source column collects full metrics: set `write.metrics.<column>=full` (or `write.metrics.default=truncate/full`) on the Iceberg table
  2. Recreate the table with default metrics (`full`/`truncate` at sufficient length) if you can control the schema/metadata
  3. Sink to a non-partitioned table, or pre-write the partition key as a top-level column with metrics enabled
  4. Check `table.properties` via pyiceberg before sinking to confirm the metric mode for the source column

Example fix

// before: metrics disabled for the partition source column
table.properties['write.metrics.category'] = 'none'
// after: require full metrics for the partition key column
table.properties['write.metrics.category'] = 'full'
lf.sink_iceberg(table)
Defensive patterns

Strategy: validation

Validate before calling

for field in table.spec().fields:
    col = table.metadata.current_schema.find_field(field.source_id).name
    mode = table.properties.get(f'write.metrics.{col}', table.properties.get('write.metrics.default', 'truncate'))
    if mode == 'none':
        raise ValueError(f"metrics mode '{mode}' for partition source column '{col}' prevents partition value inference")

Type guard

def metrics_allow_bounds(table, source_name: str) -> bool:
    mode = table.properties.get(f'write.metrics.{source_name}', table.properties.get('write.metrics.default'))
    return mode != 'none'

Try / catch

try:
    lf.sink_iceberg(table)
except NotImplementedError as e:
    if 'partition value inference requires lower and upper bounds' in str(e):
        raise RuntimeError("set write.metrics.<partition_col>=full on the table before sinking") from e
    raise

Prevention

When it happens

Trigger: Sinking to a partitioned Iceberg table when the table's metrics config (`write.metrics.default` / per-column `write.metrics.<column>`) for the partition source column is set to a mode without min/max bounds (e.g. `none`), while partition value inference is required.

Common situations: Tables created with `write.metrics.default=none` for performance; per-column metrics tuned down for wide tables accidentally applied to a partition key column; inheriting a table created by another engine with restrictive metrics settings.

Related errors


AI-assisted analysis of pola-rs/polars@fc24390824 (2026-09-02). Data as JSON: /api/errors/2b8fc70fe615abae. Report an issue: GitHub.