pinpoint-apm/pinpoint · error · IllegalStateException

Scanner is exhausted, cannot call next() again

Error message

Scanner is exhausted, cannot call next() again

What it means

LocalScannerImpl wraps a per-region ResultScanner for the row-distributor (WD) scanner. Once the underlying scanner returns null it is marked exhausted; calling next() again is a contract violation for ResultScanner semantics and throws IllegalStateException to prevent returning stale or bogus rows.

Solutions

  1. Stop iterating as soon as next() returns null
  2. Obtain a new scanner (via the WD scanner factory) instead of reusing an exhausted one
  3. Check for duplicate next() calls in retry/loop logic around ResultScanner usage

Example fix

// before
Result r = scanner.next();
process(r);
Result r2 = scanner.next(); // may throw if already exhausted
// after
Result r;
while ((r = scanner.next()) != null) { process(r); }
Defensive patterns

Strategy: validation

Try / catch

try {
    Result r = localScanner.next();
} catch (IllegalStateException e) {
    logger.error("Scanner misuse: {}", e.getMessage());
    scanner = wdScanner.getScanner(region); // reacquire instead of reusing
}

Prevention

When it happens

Trigger: Calling next() on a LocalScannerImpl after a previous next() returned null (end of the wrapped region's results), e.g. a loop that doesn't break on null or retries after exhaustion.

Common situations: Consumer loops calling next() once more to 'confirm' the end; code caching the scanner and reusing it after completion; bugs in aggregate logic over WD-scanned regions.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/58c7de1b4e95d9ed. Report an issue: GitHub.

Appendix: source

Thrown at commons-hbase/src/main/java/com/navercorp/pinpoint/common/hbase/wd/LocalScannerImpl.java:50

 * callers to peek at the next {@link Result} and query exhaustion state before
 * consuming it.
 */
public class LocalScannerImpl implements Closeable, LocalScanner {

    private final ResultScanner scanner;

    private boolean exhausted = false;

    private Result localBuffer;

    public LocalScannerImpl(ResultScanner scanner) {
        this.scanner = Objects.requireNonNull(scanner, "scanner");
    }

    @Override
    public Result next() throws IOException {
        if (exhausted) {
            throw new IllegalStateException("Scanner is exhausted, cannot call next() again");
        }
        if (localBuffer != null) {
            return localBuffer;
        }
        Result next = scanner.next();
        if (next == null) {
            exhausted = true;
        }
        localBuffer = next;
        return localBuffer;
    }

    @Override
    public boolean isExhausted() {
        return exhausted;
    }

    @Override

View on GitHub (pinned to 744c3d3075)