apache/beam · error · java.lang.UnsupportedOperationException

Does not support BeamIOSinkRel in toRowList.

Error message

Does not support BeamIOSinkRel in toRowList.

What it means

BeamEnumerableConverter.toRowList executes a Beam SQL plan and materializes results into an in-memory List<Row>. Sink rel nodes (BeamIOSinkRel, e.g. INSERT/CTAS-style statements that write to a table) do not produce enumerable rows, so calling toRowList on such a plan throws UnsupportedOperationException. The caller should instead use a path that executes the pipeline (such as toEnumerable/BeamSqlEnv execution) rather than collecting rows.

Solutions

  1. Use toEnumerable (or the sink-execution path) for BeamIOSinkRel plans instead of toRowList
  2. Restrict toRowList usage to pure SELECT queries; detect DML beforehand (e.g. check the parsed statement type) and route write statements to pipeline execution
  3. If you only need the side effect of writing, run the pipeline via PipelineResult waitUntilFinish after building from the sink rel, and ignore/empty row results
  4. Check node instanceof BeamIOSinkRel in your own wrapper before calling toRowList and fail fast with a clearer message

Example fix

// before
List<Row> rows = BeamEnumerableConverter.toRowList(options, relNode); // throws for INSERT
// after
if (relNode instanceof BeamIOSinkRel) {
  BeamEnumerableConverter.toEnumerable(options, relNode). enumerator-move/execute pipeline; // execute sink
} else {
  List<Row> rows = BeamEnumerableConverter.toRowList(options, relNode);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (relNode instanceof BeamIOSinkRel) {
  throw new IllegalArgumentException("Use pipeline/sink execution, not toRowList, for DML statements");
}

Type guard

boolean isSinkQuery(BeamRelNode node) { return node instanceof BeamIOSinkRel; }

Try / catch

try {
  rows = BeamEnumerableConverter.toRowList(options, node);
} catch (UnsupportedOperationException e) {
  if (e.getMessage().contains("BeamIOSinkRel")) {
    BeamEnumerableConverter.toEnumerable(options, node); // execute sink path
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling BeamEnumerableConverter.toRowList(options, node) (directly or via the JDBC/Shell default row-collection path) with a parsed and validated BeamRelNode that is a BeamIOSinkRel — i.e. the SQL statement is a DML sink (e.g. INSERT INTO) rather than a SELECT.

Common situations: Executing 'INSERT INTO ... SELECT ...' style statements through a code path that expects to collect rows in memory; using BeamSqlCli or the JDBC adapter configured to return row lists against a write statement; programmatically running sqlEnv.parseQuery(...).compile... and choosing toRowList for a non-SELECT statement.

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/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/fb0d51809dea8734. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamEnumerableConverter.java:160

      Thread.currentThread().setContextClassLoader(originalClassLoader);
    }
  }

  public static PipelineOptions createPipelineOptions(Map<String, String> map) {
    final String[] args = new String[map.size()];
    int i = 0;
    for (Map.Entry<String, String> entry : map.entrySet()) {
      args[i++] = "--" + entry.getKey() + "=" + entry.getValue();
    }
    PipelineOptions options = PipelineOptionsFactory.fromArgs(args).withValidation().create();
    FileSystems.setDefaultPipelineOptions(options);
    options.as(ApplicationNameOptions.class).setAppName("BeamSql");
    return options;
  }

  static List<Row> toRowList(PipelineOptions options, BeamRelNode node) {
    if (node instanceof BeamIOSinkRel) {
      throw new UnsupportedOperationException("Does not support BeamIOSinkRel in toRowList.");
    } else if (isLimitQuery(node)) {
      throw new UnsupportedOperationException("Does not support queries with LIMIT in toRowList.");
    }
    return collectRows(options, node).stream().collect(Collectors.toList());
  }

  static Enumerable<Object> toEnumerable(PipelineOptions options, BeamRelNode node) {
    if (node instanceof BeamIOSinkRel) {
      return count(options, node);
    } else if (isLimitQuery(node)) {
      return limitCollect(options, node);
    }
    return Linq4j.asEnumerable(rowToAvaticaAndUnboxValues(collectRows(options, node)));
  }

  private static PipelineResult limitRun(
      PipelineOptions options,
      BeamRelNode node,

View on GitHub (pinned to 12126d8942)