nathanmarz/storm · error · IllegalArgumentException

State spout has already been declared for id

Error message

State spout has already been declared for id ${id}

What it means

TopologyBuilder.validateUnusedId throws IllegalArgumentException when a component id collides with an already-registered state spout (_stateSpouts). Ids must be unique across all component kinds in the topology.

Solutions

  1. Use a distinct id for the state spout
  2. Ensure generated ids are namespaced per component kind (e.g. "state-" + name)
  3. Use a fresh TopologyBuilder for each build
  4. Check for duplicate setStateSpout calls with the same id

Example fix

// before
builder.setStateSpout("tx", new MyStateSpout());
builder.setSpout("tx", new TransactionalSpout()); // throws
// after
builder.setStateSpout("tx-state", new MyStateSpout());
builder.setSpout("tx", new TransactionalSpout());
Defensive patterns

Strategy: validation

Validate before calling

Set<String> used = new HashSet<>();
void safeStateSpout(TopologyBuilder b, String id, IStateSpout ss) {
    if (!used.add(id))
        throw new IllegalStateException("duplicate component id: " + id);
    b.setStateSpout(id, ss);
}

Try / catch

try {
    builder.setStateSpout(id, stateSpout);
} catch (IllegalArgumentException e) {
    if (!e.getMessage().contains("already been declared")) throw e;
    // handle duplicate id
}

Prevention

When it happens

Trigger: Declaring a state spout (or bolt/spout) with an id already present in _stateSpouts via setStateSpout; loops re-declaring state spouts with fixed ids.

Common situations: State spout id copied from a bolt id; transactional topologies declaring state spouts and spouts under the same id; builder reuse after a prior declaration.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at storm-core/src/jvm/backtype/storm/topology/TopologyBuilder.java:218

    public void setStateSpout(String id, IRichStateSpout stateSpout) {
        setStateSpout(id, stateSpout, null);
    }

    public void setStateSpout(String id, IRichStateSpout stateSpout, Number parallelism_hint) {
        validateUnusedId(id);
        // TODO: finish
    }


    private void validateUnusedId(String id) {
        if(_bolts.containsKey(id)) {
            throw new IllegalArgumentException("Bolt has already been declared for id " + id);
        }
        if(_spouts.containsKey(id)) {
            throw new IllegalArgumentException("Spout has already been declared for id " + id);
        }
        if(_stateSpouts.containsKey(id)) {
            throw new IllegalArgumentException("State spout has already been declared for id " + id);
        }
    }

    private ComponentCommon getComponentCommon(String id, IComponent component) {
        ComponentCommon ret = new ComponentCommon(_commons.get(id));
        
        OutputFieldsGetter getter = new OutputFieldsGetter();
        component.declareOutputFields(getter);
        ret.set_streams(getter.getFieldsDeclaration());
        return ret;        
    }
    
    private void initCommon(String id, IComponent component, Number parallelism) {
        ComponentCommon common = new ComponentCommon();
        common.set_inputs(new HashMap<GlobalStreamId, Grouping>());
        if(parallelism!=null) common.set_parallelism_hint(parallelism.intValue());
        Map conf = component.getComponentConfiguration();
        if(conf!=null) common.set_json_conf(JSONValue.toJSONString(conf));

View on GitHub (pinned to cdb116e942)