nathanmarz/storm · error · IllegalStateException

Expecting tx state to be initialized in strict order but…

Error message

Expecting tx state to be initialized in strict order but there are txids after that have state

What it means

Strict-order mode also demands that transactions get state in ascending order: if the requested txid has entries that come AFTER it (afterMap not empty, i.e. later txids already have state), the ordering invariant is broken and getState() throws IllegalStateException.

Solutions

  1. Remove the newer txid entries (afterMap) or restore the store so txids are initialized in strictly ascending order
  2. Wipe the transactional state and restart the topology from INIT_TXID for a clean rebuild (accepting replay semantics)
  3. Disable strict ordering (_strictOrder=false) if your spout tolerates out-of-order state initialization

Example fix

// before
rotatingState.getStateOrCreate(prevTxid); // txids 11,12 already have state
// after
for (BigInteger later : laterTxidsWithState) {
    rotatingState.removeState(later);
}
rotatingState.getStateOrCreate(prevTxid);
Defensive patterns

Strategy: validation

Validate before calling

if (strictOrder && state.existsTxidGreaterThan(txid)) {
    LOG.error("Out-of-order initialization: later txids already have state for requested txid {}", txid);
    return false;
}

Type guard

boolean isInitInOrder(RotatingTransactionalState s, BigInteger txid) {
    return !s.hasTxidsAfter(txid);
}

Try / catch

try {
    state.getStateOrCreate(txid);
} catch (IllegalStateException e) {
    LOG.error("Out-of-order tx state init for txid " + txid, e);
    resetTransactionalState(); // wipe and start from INIT_TXID
}

Prevention

When it happens

Trigger: Calling getStateOrCreate(txid) with _strictOrder=true when entries exist in the state store whose txid is greater than the requested txid (initializing an out-of-order / backdated transaction).

Common situations: A failed transaction rolled back then re-initialized with a different (lower) txid while later transactions already committed; coordinator restarted and began new transactions while orphaned state from a previous run remains; concurrent or manual cleanup deleting the middle of a txid chain.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at storm-core/src/jvm/backtype/storm/transactional/state/RotatingTransactionalState.java:86

    }
    
    public Object getState(BigInteger txid, StateInitializer init) {
        if(!_curr.containsKey(txid)) {
            SortedMap<BigInteger, Object> prevMap = _curr.headMap(txid);
            SortedMap<BigInteger, Object> afterMap = _curr.tailMap(txid);            
            
            BigInteger prev = null;
            if(!prevMap.isEmpty()) prev = prevMap.lastKey();
            
            if(_strictOrder) {
                if(prev==null && !txid.equals(TransactionalSpoutCoordinator.INIT_TXID)) {
                    throw new IllegalStateException("Trying to initialize transaction for which there should be a previous state");
                }
                if(prev!=null && !prev.equals(txid.subtract(BigInteger.ONE))) {
                    throw new IllegalStateException("Expecting previous txid state to be the previous transaction");
                }
                if(!afterMap.isEmpty()) {
                    throw new IllegalStateException("Expecting tx state to be initialized in strict order but there are txids after that have state");                
                }                
            }
            
            
            Object data;
            if(afterMap.isEmpty()) {
                Object prevData;
                if(prev!=null) {
                    prevData = _curr.get(prev);
                } else {
                    prevData = null;
                }
                data = init.init(txid, prevData);
            } else {
                data = null;
            }
            _curr.put(txid, data);
            _state.setData(txPath(txid), data);

View on GitHub (pinned to cdb116e942)