{"record":{"id":"629072611b4fe300","repo":"nathanmarz/storm","slug":"current-batch","errorCode":null,"errorMessage":"Current batch (","messagePattern":"Current batch \\(","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"storm-core/src/jvm/storm/trident/state/OpaqueValue.java","lineNumber":44,"sourceCode":"    \n    public OpaqueValue(Long currTxid, T val, T prev) {\n        this.curr = val;\n        this.currTxid = currTxid;\n        this.prev = prev;\n    }\n\n    public OpaqueValue(Long currTxid, T val) {\n        this(currTxid, val, null);\n    }\n    \n    public OpaqueValue<T> update(Long batchTxid, T newVal) {\n        T prev;\n        if(batchTxid==null || (this.currTxid < batchTxid)) {\n            prev = this.curr;\n        } else if(batchTxid.equals(this.currTxid)){\n            prev = this.prev;\n        } else {\n            throw new RuntimeException(\"Current batch (\" + batchTxid + \") is behind state's batch: \" + this.toString());\n        }\n        return new OpaqueValue<T>(batchTxid, newVal, prev);\n    }\n    \n    public T get(Long batchTxid) {\n        if(batchTxid==null || (this.currTxid < batchTxid)) {\n            return curr;\n        } else if(batchTxid.equals(this.currTxid)){\n            return prev;\n        } else {\n            throw new RuntimeException(\"Current batch (\" + batchTxid + \") is behind state's batch: \" + this.toString());\n        }\n    }\n    \n    public T getCurr() {\n        return curr;\n    }\n    ","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/nathanmarz/storm/blob/cdb116e942666973bc4eaa0df098d5bab82739e7/storm-core/src/jvm/storm/trident/state/OpaqueValue.java#L26-L62","documentation":"OpaqueValue stores current and previous values keyed by transaction id. update() computes the previous value relative to the incoming batch transaction; if the batch's txid is older than the state's current txid (and not equal), the batch is behind the state and the update is ambiguous, so a RuntimeException is thrown.","triggerScenarios":"Calling multiUpdate with a batchTxid that is neither null nor greater than nor equal to the stored currTxid — i.e. replaying a batch whose txid is older than the one already applied to this state value.","commonSituations":"Replaying old batches against opaque state after topology changes; misconfigured state storage where txids regress; using a non-opaque workflow with an opaque state.","solutions":["Ensure the state backend is opaque (OpaqueMap) if replay of older transactions is possible","Check that transaction ids advance monotonically and the spout's state in ZooKeeper is intact","If stale state exists, clear or migrate state so stored currTxid is not ahead of incoming batches"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"OpaqueValue v = state.get(key);\nif (v != null && batchTxid != null && batchTxid < v.getCurrTxid()) {\n  throw new ReplayStateException(\"batch \" + batchTxid + \" behind state txid \" + v.getCurrTxid());\n}","typeGuard":null,"tryCatchPattern":"try {\n  opaqueValue.update(batchTxid, newVal);\n} catch (RuntimeException e) {\n  if (e.getMessage().startsWith(\"Current batch (\")) {\n    LOG.error(\"Batch txid behind state; reset state or fix spout tx state\", e);\n    throw new FailedException(e); // trigger replay/cleanup\n  }\n  throw e;\n}","preventionTips":["Use OpaqueMap state wherever replay of old transactions can occur","Keep spout transaction state in ZooKeeper intact; don't wipe zk while keeping state store","Never reuse a state store across topologies with independent txid sequences"],"tags":["storm","trident","state","transaction"],"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"}