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

  1. Do not read value() from a NOOP counter; guard on the counter instance before reading
  2. Use a real MetricsContext (e.g. DefaultMetricsContext) if the value is needed
  3. Log the counter via toString() (renders 'NOOP counter') instead of value()
  4. 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

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/a9ac66e01d9a3506. Report an issue: GitHub.