nathanmarz/storm · error · RuntimeException

Regular rich spouts not supported yet... try wrapping in a…

Error message

Regular rich spouts not supported yet... try wrapping in a RichSpoutBatchExecutor

What it means

Trident topologies only accept spouts implementing IBatchSpout or ITridentSpout. During TridentTopology.build(), any spout node that is a plain Storm IRichSpout hits this else branch and a RuntimeException is thrown. Trident is batch-oriented and has no path for regular continuous spouts (the TODO comments note full support was never added).

Solutions

  1. Wrap the IRichSpout with RichSpoutBatchExecutor: TridentTopology.newStream(name, new RichSpoutBatchExecutor(spout))
  2. Implement ITridentSpout (or IBatchSpout) instead of IRichSpout
  3. Use an existing Trident-native connector for your data source

Example fix

// before
TridentTopology topology = new TridentTopology();
topology.newStream("spout", new MyRichSpout());
// after
topology.newStream("spout", new RichSpoutBatchExecutor(new MyRichSpout()));
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(spout instanceof IBatchSpout || spout instanceof ITridentSpout)) {
    spout = new RichSpoutBatchExecutor((IRichSpout) spout);
}

Type guard

boolean isTridentCompatible(Object spout) {
    return spout instanceof IBatchSpout || spout instanceof ITridentSpout;
}

Try / catch

try {
    topology.newStream("s", spout);
} catch (RuntimeException e) {
    if (e.getMessage().contains("Regular rich spouts not supported")) {
        throw new IllegalStateException("Wrap IRichSpout in RichSpoutBatchExecutor before use", e);
    } throw e;
}

Prevention

When it happens

Trigger: newStream() fed from a spout created via topology.addSpout or TridentTopology.newStream(name, irichSpout) where the spout only implements IRichSpout, then calling build().

Common situations: Migrating an existing Storm topology to Trident and reusing the old IRichSpout; third-party connector spouts written for core Storm; tutorials mixing Trident API with classic spout implementations.

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 nathanmarz/storm@cdb116e942 (2026-09-12). Data as JSON: /api/errors/1c0465b6e54ef34c. Report an issue: GitHub.

Appendix: source

Thrown at storm-core/src/jvm/storm/trident/TridentTopology.java:398

        
        TridentTopologyBuilder builder = new TridentTopologyBuilder();
        
        Map<Node, String> spoutIds = genSpoutIds(spoutNodes);
        Map<Group, String> boltIds = genBoltIds(mergedGroups);
        
        for(SpoutNode sn: spoutNodes) {
            Integer parallelism = parallelisms.get(grouper.nodeGroup(sn));
            if(sn.type == SpoutNode.SpoutType.DRPC) {
                builder.setBatchPerTupleSpout(spoutIds.get(sn), sn.streamId,
                        (IRichSpout) sn.spout, parallelism, batchGroupMap.get(sn));
            } else {
                ITridentSpout s;
                if(sn.spout instanceof IBatchSpout) {
                    s = new BatchSpoutExecutor((IBatchSpout)sn.spout);
                } else if(sn.spout instanceof ITridentSpout) {
                    s = (ITridentSpout) sn.spout;
                } else {
                    throw new RuntimeException("Regular rich spouts not supported yet... try wrapping in a RichSpoutBatchExecutor");
                    // TODO: handle regular rich spout without batches (need lots of updates to support this throughout)
                }
                builder.setSpout(spoutIds.get(sn), sn.streamId, sn.txId, s, parallelism, batchGroupMap.get(sn));
            }
        }
        
        for(Group g: mergedGroups) {
            if(!isSpoutGroup(g)) {
                Integer p = parallelisms.get(g);
                Map<String, String> streamToGroup = getOutputStreamBatchGroups(g, batchGroupMap);
                BoltDeclarer d = builder.setBolt(boltIds.get(g), new SubtopologyBolt(graph, g.nodes, batchGroupMap), p,
                        committerBatches(g, batchGroupMap), streamToGroup);
                Collection<PartitionNode> inputs = uniquedSubscriptions(externalGroupInputs(g));
                for(PartitionNode n: inputs) {
                    Node parent = TridentUtils.getParent(graph, n);
                    String componentId;
                    if(parent instanceof SpoutNode) {
                        componentId = spoutIds.get(parent);

View on GitHub (pinned to cdb116e942)