apache/iceberg · error · UnsupportedOperationException
NOOP counter has no value
Error message
NOOP counter has no value
What it means
DefaultCounter.NOOP is a non-recording counter; increment() discards data, so value() has no meaningful result and throws UnsupportedOperationException. toString is overridden to render 'NOOP counter' so callers who stringify never hit the throw. Calling value() on the NOOP counter is a programming error: metrics were explicitly disabled or routed to a no-op context.
Source
Thrown at api/src/main/java/org/apache/iceberg/metrics/DefaultCounter.java:39
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import org.apache.iceberg.metrics.MetricsContext.Unit;
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
/** A default {@link Counter} implementation that uses an {@link AtomicLong} to count events. */
public class DefaultCounter implements Counter {
public static final Counter NOOP =
new DefaultCounter(Unit.UNDEFINED) {
@Override
public void increment() {}
@Override
public void increment(long amount) {}
@Override
public long value() {
throw new UnsupportedOperationException("NOOP counter has no value");
}
@Override
public String toString() {
return "NOOP counter";
}
};
private final LongAdder counter;
private final MetricsContext.Unit unit;
private AsIntCounter asIntCounter = null;
private AsLongCounter asLongCounter = null;
DefaultCounter(MetricsContext.Unit unit) {
Preconditions.checkArgument(null != unit, "Invalid count unit: null");
this.unit = unit;
this.counter = new LongAdder();
}View on GitHub (pinned to 86d9c8fc54)
Solutions
- Do not read value() from a NOOP counter; guard on the counter instance before reading
- Use a real MetricsContext (e.g. DefaultMetricsContext) if the value is needed
- Log the counter via toString() (renders 'NOOP counter') instead of value()
- Check whether metrics are intentionally disabled via configuration before consuming values
Example fix
// before
long v = counter.value();
// after
if (counter != DefaultCounter.NOOP) {
long v = counter.value();
} Defensive patterns
Strategy: type-guard
Validate before calling
if (counter == DefaultCounter.NOOP) { skip; } Type guard
boolean isNoopCounter(Counter<?> c) { return c == DefaultCounter.NOOP; } Try / catch
try { long v = counter.value(); } catch (UnsupportedOperationException e) { log.info("metrics disabled"); } Prevention
- Guard value() reads when metrics may be disabled
- Prefer toString for display of counters
- Use DefaultMetricsContext when values matter
- Keep NOOP usage centralized
When it happens
Trigger: Calling value() on DefaultCounter.NOOP (obtained via LoggingContext/SimpleContext unsupported defaults or directly), typically for logging or assertions on the metric value.
Common situations: Code that assumes a real counter was returned from MetricsContext.counter(); tests asserting on metric values while metrics are disabled; sharing a counter across code paths where one path is no-op.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Count is not supported.
- Counter is not supported.
- Timer is not supported.
- Histogram is not supported.
- NOOP timer has no count
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/a9ac66e01d9a3506.
Report an issue: GitHub.