{"record":{"id":"6dacd475399c7500","repo":"nathanmarz/storm","slug":"expecting-previous-txid-state-to-be-the-previous-transaction","errorCode":null,"errorMessage":"Expecting previous txid state to be the previous transaction","messagePattern":"Expecting previous txid state to be the previous transaction","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"storm-core/src/jvm/backtype/storm/transactional/state/RotatingTransactionalState.java","lineNumber":83,"sourceCode":"            _curr.remove(txid);\n            _state.delete(txPath(txid));\n        }\n    }\n    \n    public Object getState(BigInteger txid, StateInitializer init) {\n        if(!_curr.containsKey(txid)) {\n            SortedMap<BigInteger, Object> prevMap = _curr.headMap(txid);\n            SortedMap<BigInteger, Object> afterMap = _curr.tailMap(txid);            \n            \n            BigInteger prev = null;\n            if(!prevMap.isEmpty()) prev = prevMap.lastKey();\n            \n            if(_strictOrder) {\n                if(prev==null && !txid.equals(TransactionalSpoutCoordinator.INIT_TXID)) {\n                    throw new IllegalStateException(\"Trying to initialize transaction for which there should be a previous state\");\n                }\n                if(prev!=null && !prev.equals(txid.subtract(BigInteger.ONE))) {\n                    throw new IllegalStateException(\"Expecting previous txid state to be the previous transaction\");\n                }\n                if(!afterMap.isEmpty()) {\n                    throw new IllegalStateException(\"Expecting tx state to be initialized in strict order but there are txids after that have state\");                \n                }                \n            }\n            \n            \n            Object data;\n            if(afterMap.isEmpty()) {\n                Object prevData;\n                if(prev!=null) {\n                    prevData = _curr.get(prev);\n                } else {\n                    prevData = null;\n                }\n                data = init.init(txid, prevData);\n            } else {\n                data = null;","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/nathanmarz/storm/blob/cdb116e942666973bc4eaa0df098d5bab82739e7/storm-core/src/jvm/backtype/storm/transactional/state/RotatingTransactionalState.java#L65-L101","documentation":"In strict-order mode, getState() requires that if a previous txid has state, the requested txid must be exactly prev+1 (txids are BigInteger and consecutive). A gap means transactions were skipped or state is corrupted, so it throws IllegalStateException.","triggerScenarios":"Calling getStateOrCreate(txid) with _strictOrder=true when prevMap.lastKey() (prev) exists but txid is not prev + 1 (e.g. txid = prev.subtract(1) on retry with stale state, or a jumped txid).","commonSituations":"Retrying an already-committed transaction with a stale/newer txid; state residue left after topology restart combined with coordinator re-emission; manual state surgery deleting one transaction's entry creating a gap; mixing old and new transactional metadata after a version change.","solutions":["Clean the transactional state store so it contains a contiguous chain of txid states matching what the coordinator will request","Ensure the coordinator's txid sequence is consecutive BigInteger values starting at INIT_TXID (txid = txid.add(ONE)) and never regenerate old txids","If you only need reads of previous state, use getStateOrNull/isCommitted instead of strict getStateOrCreate"],"exampleFix":"// before\nBigInteger next = BigInteger.valueOf(50); // prev stored state is 48 -> gap\nrotatingState.getStateOrCreate(next);\n// after\nBigInteger next = lastTxid.add(BigInteger.ONE);\nrotatingState.getStateOrCreate(next);","handlingStrategy":"validation","validationCode":"BigInteger prev = getLatestStoredTxid();\nif (prev != null && !txid.equals(prev.add(BigInteger.ONE))) {\n    LOG.error(\"txid {} does not follow stored prev {}\", txid, prev);\n    return false;\n}","typeGuard":"boolean isConsecutive(BigInteger prev, BigInteger txid) {\n    return prev == null || prev.add(BigInteger.ONE).equals(txid);\n}","tryCatchPattern":"try {\n    state.getStateOrCreate(txid);\n} catch (IllegalStateException e) {\n    LOG.error(\"Non-consecutive txid {} in strict rotating state\", txid, e);\n    rebuildStateFromScratch();\n}","preventionTips":["Generate txids strictly as previousTxid + 1 from INIT_TXID","Never reuse or regenerate old txids after coordinator restart","Clean stale state when switching between topology versions that number transactions differently"],"tags":["storm","transactional-spout","txid","state","zookeeper"],"backgroundTag":"invalid-state-transition","analyzedSha":"cdb116e942666973bc4eaa0df098d5bab82739e7","analyzedAt":"2026-09-12T14:30:00.714Z","contentChangedAt":"2026-09-12T14:30:00.714Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}