{"record":{"id":"3e7004122a7c2858","repo":"Netflix/Hystrix","slug":"response-has-already-terminated-so-exception-can-n","errorCode":null,"errorMessage":"Response has already terminated so exception can not be set","messagePattern":"Response has already terminated so exception can not be set","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hystrix-core/src/main/java/com/netflix/hystrix/collapser/CollapsedRequestSubject.java","lineNumber":174,"sourceCode":"            setExceptionIfResponseNotReceived(exception);\n        }\n        // return any exception that was generated\n        return exception;\n    }\n\n    /**\n     * When set any client thread blocking on get() will immediately be unblocked and receive the exception.\n     * \n     * @throws IllegalStateException\n     *             if called more than once or after setResponse.\n     * @param e received exception that gets set on the initial command\n     */\n    @Override\n    public void setException(Exception e) {\n        if (!isTerminated()) {\n            subject.onError(e);\n        } else {\n            throw new IllegalStateException(\"Response has already terminated so exception can not be set\", e);\n        }\n    }\n\n    private boolean isTerminated() {\n        return (subject.hasCompleted() || subject.hasThrowable());\n    }\n\n    public Observable<T> toObservable() {\n        return subjectWithAccounting;\n    }\n}","sourceCodeStart":156,"sourceCodeEnd":185,"githubUrl":"https://github.com/Netflix/Hystrix/blob/5ce3bc58c38e7ca60ef2fe0e516e390e294ad941/hystrix-core/src/main/java/com/netflix/hystrix/collapser/CollapsedRequestSubject.java#L156-L185","documentation":"CollapsedRequestSubject.setException() delivers a failure to a single collapsed request and is legal only pre-termination; after the request was completed or already errored it throws IllegalStateException('Response has already terminated so exception can not be set'). The original exception is attached as the cause. Typically a double-termination bug in mapResponseToRequests.","triggerScenarios":"setException() invoked after setResponse/setComplete/setException already terminated that request — e.g. generic catch-all loops that set exceptions on ALL requests including ones already satisfied earlier in the batch mapping.","commonSituations":"Batch command fails wholesale and code sets the exception on every request, while some requests already got responses from a partial-success path; retry/fallback flows inside the collapser that re-set the exception.","solutions":["Set the exception only on requests that have not yet been terminated; filter with a completed-set or check subject state via toObservable() materialization","On batch failure, set exceptions OR responses, never both for the same request","Write tests covering partial batch failure and duplicate mapping"],"exampleFix":"// before\ntry { batchResponse.forEach(r -> request.setResponse(r)); }\ncatch (Exception e) { requests.forEach(q -> q.setException(e)); } // some already set -> ISE\n// after\nSet<CollapsedRequest<?, ?>> done = ...;\ntry { batchResponse.forEach(r -> { request.setResponse(r); done.add(request); }); }\ncatch (Exception e) { for (q : requests) if (!done.contains(q)) q.setException(e); }","handlingStrategy":"validation","validationCode":"Set<CollapsedRequest<?, ?>> terminated = new HashSet<>();\n// success path: request.setResponse(v); terminated.add(request);\n// failure path: if (!terminated.contains(request)) request.setException(e);","typeGuard":"null","tryCatchPattern":"catch (IllegalStateException e) { if (e.getMessage().contains(\"exception can not be set\")) { /* request already resolved — ignore duplicate error, log it */ } }","preventionTips":["On batch failure, set exceptions only on unresolved requests","Wrap per-request completion in a single idempotent helper method"],"tags":["java","hystrix","collapser","exception-handling","lifecycle"],"backgroundTag":null,"analyzedSha":"5ce3bc58c38e7ca60ef2fe0e516e390e294ad941","analyzedAt":"2026-08-14T10:55:35.600Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}