apache/seatunnel · error · ClickhouseConnectorException

never happen error !

Error message

never happen error !

What it means

This ClickhouseConnectorException with SHOULD_NEVER_HAPPEN is thrown by ClickhouseValueReader.next() when the internal rowBatch field is null. It signals an internal invariant violation: the reader's batching strategy was never initialized (no prior hasNext()/read produced a batch), so there is no row batch to return. Callers must always drive the reader through hasNext() before calling next().

Solutions

  1. Always call hasNext() and only call next() when it returns true — this initializes rowBatch via partBatchStrategyRead()/sqlBatchStrategyRead()
  2. Check that the split was properly opened/initialized before reading (reader state set up in the split enumerator/reader lifecycle)
  3. If this occurs in normal SeaTunnel flow, enable debug logging on the reader and file a bug with the split/shard info since it indicates an internal state bug
  4. Ensure the split being read actually matched a ClickHouse part/shard so a batching strategy was selected

Example fix

// before
List<SeaTunnelRow> rows = reader.next();
// after
if (reader.hasNext()) {
    List<SeaTunnelRow> rows = reader.next();
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Java: guard before consuming
if (!reader.hasNext()) {
    throw new IllegalStateException("Reader has no batch; hasNext() must be called before next()");
}

Type guard

boolean canRead(ClickhouseValueReader reader) {
    try {
        return reader.hasNext();
    } catch (Exception e) {
        return false;
    }
}

Try / catch

try {
    rows = reader.next();
} catch (ClickhouseConnectorException e) {
    if (e.getErrorCode() == ClickhouseConnectorErrorCode.SHOULD_NEVER_HAPPEN) {
        // reinitialize the reader/split and retry from checkpoint
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling next() on a ClickhouseValueReader whose rowBatch is null — i.e., next() is invoked before any successful hasNext()/batch-read call, or after the reader was constructed but never advanced, or when no batching strategy was configured.

Common situations: Custom or patched source reader code that calls next() without first calling hasNext(); a reader deserialized/reinitialized after failure where rowBatch was never populated; misuse of the reader API outside the normal SourceReader poll loop.

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


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/36c9b65cd5ba0a94. Report an issue: GitHub.

Appendix: source

Thrown at seatunnel-connectors-v2/connector-clickhouse/src/main/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/source/ClickhouseValueReader.java:109

        this.shouldUseStreamReader = shouldUseStreamReader();
    }

    public boolean hasNext() {
        if (shouldUseStreamReader) {
            if (streamValueReader == null) {
                streamValueReader = new StreamValueReader();
            }
            return streamValueReader.hasNext();
        } else if (clickhouseSourceTable.isSqlStrategyRead()) {
            return sqlBatchStrategyRead();
        } else {
            return partBatchStrategyRead();
        }
    }

    public List<SeaTunnelRow> next() {
        if (rowBatch == null) {
            throw new ClickhouseConnectorException(
                    ClickhouseConnectorErrorCode.SHOULD_NEVER_HAPPEN, "never happen error !");
        }

        return rowBatch;
    }

    private boolean partBatchStrategyRead() {
        List<ClickhousePart> parts = clickhouseSourceSplit.getParts();
        int partSize = parts.size();

        if (currentPartIndex >= partSize) {
            return false;
        }

        ClickhousePart currentPart = parts.get(currentPartIndex);

        // If current part has been processed, move to the next part
        if (currentPart.isEndOfPart()) {

View on GitHub (pinned to cf67b549a7)