nathanmarz/storm · error · RuntimeException

Each operation can only have one parent

Error message

Each operation can only have one parent

What it means

EachProcessor.prepare requires exactly one parent tuple factory, because each() applies a function to a single input stream. When the Trident context reports a parent count other than 1, prepare throws this RuntimeException. The topology graph routes more than one stream (or none) into this each() node.

Solutions

  1. Apply each() to a single Stream chain; move each() before any merge/join that creates multiple parents.
  2. If both streams need the function, apply each() to each stream separately before merging.
  3. Use each() on the output of a join node rather than wiring two streams into one each node.
  4. Audit custom Node/Edge construction so each EachProcessor node has in-degree 1.

Example fix

// before
Stream both = s1; // graph wired with parents s1 and s2
both.each(getFields, func);
// after
Stream a = s1.each(getFields, func);
Stream b = s2.each(getFields, func);
Stream out = a.merge(b);
Defensive patterns

Strategy: validation

Validate before calling

// Java: structural pre-check in custom graph builders
if (eachNode.getParents().size() != 1) {
    throw new IllegalArgumentException("each() node must have exactly one parent, got " + eachNode.getParents().size());
}

Try / catch

try { stream.each(...); } catch (RuntimeException e) { if (e.getMessage().contains("can only have one parent")) { throw new TopologyStructureException("each() must sit on a single-parent stream", e); } throw e; }

Prevention

When it happens

Trigger: A Trident topology where a each() node is wired to multiple parent streams, typically from programmatic graph construction or applying each() to a stream object that internally merged several parents without a single-parent intermediate node.

Common situations: Custom Trident planner/graph code; connecting two Streams into one Each node manually; version upgrades of Trident internals changing merge semantics so each() ends up with two parents.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of nathanmarz/storm@cdb116e942 (2026-09-12). Data as JSON: /api/errors/400a380b9ebea0d3. Report an issue: GitHub.

Appendix: source

Thrown at storm-core/src/jvm/storm/trident/planner/processor/EachProcessor.java:49


public class EachProcessor implements TridentProcessor {
    Function _function;
    TridentContext _context;
    AppendCollector _collector;
    Fields _inputFields;
    ProjectionFactory _projection;
    
    public EachProcessor(Fields inputFields, Function function) {
        _function = function;
        _inputFields = inputFields;
    }
    
    @Override
    public void prepare(Map conf, TopologyContext context, TridentContext tridentContext) {
        List<Factory> parents = tridentContext.getParentTupleFactories();
        if(parents.size()!=1) {
            throw new RuntimeException("Each operation can only have one parent");
        }
        _context = tridentContext;
        _collector = new AppendCollector(tridentContext);
        _projection = new ProjectionFactory(parents.get(0), _inputFields);
        _function.prepare(conf, new TridentOperationContext(context, _projection));
    }

    @Override
    public void cleanup() {
        _function.cleanup();
    }    

    @Override
    public void execute(ProcessorContext processorContext, String streamId, TridentTuple tuple) {
        _collector.setContext(processorContext, tuple);
        _function.execute(_projection.create(tuple), _collector);
    }

View on GitHub (pinned to cdb116e942)