apache/iceberg · error · IllegalStateException
OptionWriter should only expect at most one field metric…
Error message
OptionWriter should only expect at most one field metric from a primitive writer.Current number of fields: %s, primitive writer type: %s
What it means
OptionWriter wraps a primitive writer for optional (nullable) data and merges column metrics from the underlying writer. It assumes the wrapped primitive writer reports at most one field metric. If the wrapped writer exposes more than one field metric (or an unexpected count), an IllegalStateException is thrown naming the field count and writer class.
Solutions
- Fix the wrapped writer so it reports exactly one (or zero) FieldMetrics entries
- Do not wrap multi-field writers in OptionWriter; handle optionality at a higher level
- If the custom writer legitimately produces multiple metrics, bypass or extend OptionWriter's metric aggregation
Example fix
// before: custom writer reports two field metrics inside OptionWriter
return new OptionWriter<>(writer, cardinality()) where writer has 2 fieldMetrics
// after: report a single metric per primitive writer
public List<FieldMetrics<?>> fields() { return ImmutableList.of(singleMetric); } Defensive patterns
Strategy: validation
Validate before calling
List<FieldMetrics<?>> metrics = primitiveWriter.fields();
Preconditions.checkState(metrics.size() <= 1,
"Writer %s must report at most one field metric, got %s",
primitiveWriter.getClass().getSimpleName(), metrics.size()); Try / catch
try {
optionWriter.addUpdate(...);
} catch (IllegalStateException e) {
if (e.getMessage().contains("OptionWriter should only expect")) {
// log writer class + metrics count; disable metric collection for this writer
} else throw e;
} Prevention
- Keep custom primitive writers to a single FieldMetrics each
- Unit-test metric collection of every new ParquetValueWriter
- Never wrap multi-column writers in OptionWriter
When it happens
Trigger: Constructing an OptionWriter around a primitive ParquetValueWriter whose fieldWriters()/metrics() return more than one FieldMetrics entry — typically after adding a custom primitive writer that reports multiple metrics or misreporting the number of fields in a composite writer.
Common situations: Custom Parquet writer implementations returning incorrect metrics counts; a bug when combining optional writers with metric-collecting writers (e.g. floating-point min/max collectors); refactors that make a 'primitive' writer internally multi-column.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Failed to read footer of file
- [Parquet bug] Stream is bad: incorrect bytes remaining
- AlwaysFalse is a placeholder only
- AlwaysTrue is a placeholder only
- Avro writer does not support variant types
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/dc4337cc6f17f9fa.
Report an issue: GitHub.
Appendix: source
Thrown at parquet/src/main/java/org/apache/iceberg/parquet/ParquetValueWriters.java:489
writer.metrics().collect(Collectors.toList());
if (fieldMetricsFromWriter.isEmpty()) {
// we are not tracking field metrics for this type ourselves
return Stream.empty();
} else if (fieldMetricsFromWriter.size() == 1) {
FieldMetrics<?> metrics = fieldMetricsFromWriter.get(0);
return Stream.of(
new FieldMetrics<>(
metrics.id(),
metrics.valueCount() + nullValueCount,
nullValueCount,
metrics.nanValueCount(),
metrics.lowerBound(),
metrics.upperBound(),
metrics.originalType(),
metrics.avgValueSizeInBytes()));
} else {
throw new IllegalStateException(
String.format(
"OptionWriter should only expect at most one field metric from a primitive writer."
+ "Current number of fields: %s, primitive writer type: %s",
fieldMetricsFromWriter.size(), writer.getClass().getSimpleName()));
}
}
// skipping updating null stats for non-primitive types since we don't use them today, to
// avoid unnecessary work
return writer.metrics();
}
}
public abstract static class RepeatedWriter<L, E> implements ParquetValueWriter<L> {
private final int definitionLevel;
private final int repetitionLevel;
private final ParquetValueWriter<E> writer;
private final List<TripleWriter<?>> children;View on GitHub (pinned to 86d9c8fc54)