{"record":{"id":"a37e8a24075a8d80","repo":"apache/pulsar","slug":"expected-transactionv5-got-txn-getclass","errorCode":null,"errorMessage":"Expected TransactionV5, got: + txn.getClass()","messagePattern":"Expected TransactionV5, got: \\+ txn\\.getClass\\(\\)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/TransactionV5.java","lineNumber":98,"sourceCode":"            case COMMITTING -> State.COMMITTING;\n            case ABORTING -> State.ABORTING;\n            case COMMITTED -> State.COMMITTED;\n            case ABORTED -> State.ABORTED;\n            case ERROR -> State.ERROR;\n            case TIME_OUT -> State.TIMED_OUT;\n        };\n    }\n\n    /**\n     * Unwrap the v4 transaction from a V5 handle, validating type. Returns null when\n     * the handle is null (no transaction context).\n     */\n    static org.apache.pulsar.client.api.transaction.Transaction unwrap(Transaction txn) {\n        if (txn == null) {\n            return null;\n        }\n        if (!(txn instanceof TransactionV5 v5)) {\n            throw new IllegalArgumentException(\"Expected TransactionV5, got: \" + txn.getClass());\n        }\n        return v5.v4Transaction;\n    }\n\n    private final class AsyncView implements AsyncTransaction {\n        @Override\n        public CompletableFuture<Void> commit() {\n            return v4Transaction.commit();\n        }\n\n        @Override\n        public CompletableFuture<Void> abort() {\n            return v4Transaction.abort();\n        }\n    }\n}\n","sourceCodeStart":80,"sourceCodeEnd":115,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/TransactionV5.java#L80-L115","documentation":"TransactionV5.unwrap() downcasts an opaque org.apache.pulsar.client.api.transaction.Transaction to the internal TransactionV5 to access the underlying v4 transaction. If the object is not a TransactionV5 instance (and is not null), it throws IllegalArgumentException with the concrete class name. Passing a v4-client transaction or a foreign Transaction implementation triggers this.","triggerScenarios":"Calling unwrap() (package-internal helper) with a Transaction obtained from the v4 client API, a mock/stub, or any custom Transaction implementation instead of the v5 wrapper created by TransactionV5 flows.","commonSituations":"Mixing pulsar-client v4 and v5 APIs in one application; test doubles that implement the Transaction interface; refactoring that replaced a v5 transaction with a base-API transaction before calling internal helpers.","solutions":["Pass only TransactionV5 instances (obtained via the v5 client's newTransaction flow) to unwrap().","If holding a raw Transaction, route it through the v5 wrapper API instead of calling unwrap directly.","Fix test mocks to wrap the v4 transaction the same way TransactionV5 does, or mock TransactionV5 itself."],"exampleFix":"// before\norg.apache.pulsar.client.api.transaction.Transaction txn = pulsarClient.newTransaction();\nvar inner = TransactionV5.unwrap(txn); // IllegalArgumentException\n// after\nAsyncTransaction txn = streamClient.newTransaction();\nvar inner = TransactionV5.unwrap((TransactionV5) txn.rawTransaction());","handlingStrategy":"type-guard","validationCode":"if (!(txn instanceof TransactionV5)) {\n    throw new IllegalArgumentException(\"unwrap requires a TransactionV5, got \" + txn.getClass());\n}","typeGuard":"static TransactionV5 asV5(Transaction txn) {\n    return (txn instanceof TransactionV5 v5) ? v5 : null;\n}","tryCatchPattern":"try {\n    var inner = TransactionV5.unwrap(txn);\n} catch (IllegalArgumentException e) {\n    log.error(\"wrong transaction type: {}\", e.getMessage());\n}","preventionTips":["Only pass transactions created through the v5 client API to unwrap()","Do not mix pulsar-client v4 and v5 transaction objects in one flow","Use the type guard before calling unwrap in shared/legacy code paths","In tests, mock TransactionV5 rather than the base Transaction interface"],"tags":["transaction","type-mismatch","illegal-argument","api-mixing"],"backgroundTag":"wrong-transaction-type","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}