{"id":"9b23fae8923a5b54","repo":"apache/kafka","slug":"the-argument-to-complete-can-not-be-an-instance-of","errorCode":null,"errorMessage":"The argument to complete can not be an instance of RuntimeException","messagePattern":"The argument to complete can not be an instance of RuntimeException","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/RequestFuture.java","lineNumber":125,"sourceCode":"     * @throws IllegalStateException if the future is not complete or completed successfully\n     */\n    public RuntimeException exception() {\n        if (!failed())\n            throw new IllegalStateException(\"Attempt to retrieve exception from future which hasn't failed\");\n        return (RuntimeException) result.get();\n    }\n\n    /**\n     * Complete the request successfully. After this call, {@link #succeeded()} will return true\n     * and the value can be obtained through {@link #value()}.\n     * @param value corresponding value (or null if there is none)\n     * @throws IllegalStateException if the future has already been completed\n     * @throws IllegalArgumentException if the argument is an instance of {@link RuntimeException}\n     */\n    public void complete(T value) {\n        try {\n            if (value instanceof RuntimeException)\n                throw new IllegalArgumentException(\"The argument to complete can not be an instance of RuntimeException\");\n\n            if (!result.compareAndSet(INCOMPLETE_SENTINEL, value))\n                throw new IllegalStateException(\"Invalid attempt to complete a request future which is already complete\");\n            fireSuccess();\n        } finally {\n            completedLatch.countDown();\n        }\n    }\n\n    /**\n     * Raise an exception. The request will be marked as failed, and the caller can either\n     * handle the exception or throw it.\n     * @param e corresponding exception to be passed to caller\n     * @throws IllegalStateException if the future has already been completed\n     */\n    public void raise(RuntimeException e) {\n        try {\n            if (e == null)","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/RequestFuture.java#L107-L143","documentation":"Thrown by RequestFuture.complete(T value) when the value passed in is itself a RuntimeException. The future uses result instanceof RuntimeException internally to distinguish failure from success (see failed()), so accepting a RuntimeException as a success value would corrupt that invariant and make succeeded()/failed() report contradictory state. Callers must signal failure through raise(RuntimeException), not complete().","triggerScenarios":"Calling future.complete(someRuntimeException); generic adapters that forward an arbitrary payload of type T where T is or extends RuntimeException; reflection or generified helper code that passes the wrong object into complete().","commonSituations":"Refactoring a pipeline so an exception object flows into a code path that calls complete() instead of raise(); custom RequestFutureAdapter implementations whose onSuccess wraps exceptions; passing a kafka Errors.exception() result into complete() by mistake.","solutions":["Route exception payloads through future.raise(e) (or RequestFuture.failure(e)) and reserve complete() for real success values.","If you are writing a generic adapter, branch on instanceof RuntimeException before deciding complete() vs raise().","Check the static type of T; if T can be a RuntimeException, narrow the type so success and failure paths are disjoint."],"exampleFix":"// before\nRequestFuture<RuntimeException> f = ...;\nf.complete(maybeAnException); // throws if value is RuntimeException\n\n// after\nif (value instanceof RuntimeException) {\n    future.raise((RuntimeException) value);\n} else {\n    future.complete(value);\n}","handlingStrategy":"type-guard","validationCode":"// Before calling complete(value), ensure value is not a RuntimeException.\nif (value instanceof RuntimeException) {\n    // use raise((RuntimeException) value) instead, which is the correct API\n    future.raise((RuntimeException) value);\n} else {\n    future.complete(value);\n}","typeGuard":"// Guard that rejects RuntimeException payloads at compile-friendly boundary.\njava.util.function.BiConsumer<RequestFuture<Object>, Object> safeComplete = (f, v) -> {\n    if (v instanceof RuntimeException) {\n        throw new IllegalArgumentException(\n            \"Use RequestFuture.raise() for failures, not complete()\");\n    }\n    f.complete(v);\n};","tryCatchPattern":"try {\n    future.complete(value);\n} catch (IllegalArgumentException ex) {\n    // value was a RuntimeException; route it through raise() instead\n    if (value instanceof RuntimeException) {\n        future.raise((RuntimeException) value);\n    }\n}","preventionTips":["Reserve complete() for success payloads and raise() for failures — never mix them.","Avoid Object-typed value variables flowing into complete(); keep success and failure types distinct.","Remember that RequestFuture uses RuntimeException internally to mark the failed state, so a RuntimeException payload is ambiguous by design."],"tags":["consumer","async","future-state","misuse"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}