{"id":"442702514354e7b2","repo":"apache/kafka","slug":"invalid-producer-id-and-epoch-values-producerid","errorCode":null,"errorMessage":"Invalid producer ID and epoch values: {producerId}:{epoch}. Both must be >= 0","messagePattern":"Invalid producer ID and epoch values: (.+?):(.+?)\\. Both must be >= 0","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/PreparedTxnState.java","lineNumber":63,"sourceCode":"    public PreparedTxnState(String serializedState) {\n        if (serializedState == null || serializedState.isEmpty()) {\n            this.producerId = RecordBatch.NO_PRODUCER_ID;\n            this.epoch = RecordBatch.NO_PRODUCER_EPOCH;\n            return;\n        }\n\n        try {\n            String[] parts = serializedState.split(\":\");\n            if (parts.length != 2) {\n                throw new IllegalArgumentException(\"Invalid serialized transaction state format: \" + serializedState);\n            }\n\n            this.producerId = Long.parseLong(parts[0]);\n            this.epoch = Short.parseShort(parts[1]);\n\n            // Validate the producerId and epoch values.\n            if (!(this.producerId >= 0 && this.epoch >= 0)) {\n                throw new IllegalArgumentException(\"Invalid producer ID and epoch values: \" +\n                    producerId + \":\" + epoch + \". Both must be >= 0\");\n            }\n        } catch (NumberFormatException e) {\n            throw new IllegalArgumentException(\"Invalid serialized transaction state format: \" + serializedState, e);\n        }\n    }\n\n    /**\n     * Creates a new PreparedTxnState with the given producer ID and epoch\n     *\n     * @param producerId        The producer ID\n     * @param epoch             The producer epoch\n     */\n    PreparedTxnState(long producerId, short epoch) {\n        this.producerId = producerId;\n        this.epoch = epoch;\n    }\n","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/PreparedTxnState.java#L45-L81","documentation":"Thrown by the PreparedTxnState(String) constructor when deserializing a two-phase-commit transaction state string of the form 'producerId:epoch' where either the parsed producerId or epoch is negative. Kafka reserves negative sentinels (NO_PRODUCER_ID=-1, NO_PRODUCER_EPOCH=-1) to mean 'uninitialized', and a real prepared transaction must always carry non-negative id and epoch, so a negative value signals corrupt or hand-edited state.","triggerScenarios":"Constructing `new PreparedTxnState(\"-1:5\")`, `new PreparedTxnState(\"10:-3\")`, or any string where the first or second ':'-separated token parses to a negative long/short. The check at PreparedTxnState.java:62 is reached only after parts.length==2 and successful Long.parseLong/Short.parseShort.","commonSituations":"Persisting the toString() of an uninitialized PreparedTxnState that was manually rewritten, replaying a checkpoint/offset store whose producer state got corrupted, or migrating state files between incompatible Kafka versions where the sentinel conventions differ.","solutions":["Inspect the serializedState string being passed in and confirm it came from PreparedTxnState.toString() of an initialized transaction (non-empty, 'id:epoch' with both >=0).","Treat empty/null strings as 'no transaction' by passing them through unchanged instead of substituting negative sentinel values.","Regenerate the state from the transactional coordinator / InitProducerId rather than restoring a tampered checkpoint."],"exampleFix":"// before\nnew PreparedTxnState(\"-1:-1\")\n\n// after\nnew PreparedTxnState(\"\")  // or omit / pass null to mean 'uninitialized'","handlingStrategy":"validation","validationCode":"// Validate a persisted 'producerId:epoch' string BEFORE constructing PreparedTxnState.\nstatic boolean isValidPreparedTxnState(String s) {\n    if (s == null || s.isEmpty()) return true; // empty == uninitialized, always legal\n    String[] parts = s.split(\":\", -1);\n    if (parts.length != 2) return false;\n    try {\n        long pid = Long.parseLong(parts[0]);\n        short epoch = Short.parseShort(parts[1]);\n        return pid >= 0 && epoch >= 0; // both must be non-negative\n    } catch (NumberFormatException e) {\n        return false;\n    }\n}\n// Caller:\nif (!isValidPreparedTxnState(stored)) { /* re-initialize txn instead of constructing */ }","typeGuard":null,"tryCatchPattern":"// PreparedTxnState(String) throws IllegalArgumentException on bad input.\ntry {\n    PreparedTxnState state = new PreparedTxnState(stored);\n} catch (IllegalArgumentException e) {\n    // Treat as no transaction: fall back to a fresh empty state.\n    log.warn(\"Discarding corrupt prepared txn state '{}'\", stored, e);\n    state = new PreparedTxnState();\n}","preventionTips":["Only persist the value returned by PreparedTxnState.toString(); never hand-format 'producerId:epoch'.","Treat external/persisted transaction state as untrusted — validate or wrap construction in try/catch.","If you load state from disk/DB, prefer storing producerId and epoch as separate typed columns rather than a delimited string."],"tags":["kafka","producer","transactions","two-phase-commit","configuration"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}