{"record":{"id":"fc07033ff54f28b7","repo":"OpenFeign/feign","slug":"throwable-error-from-subscription-stream-rethrown","errorCode":null,"errorMessage":"Throwable error from subscription stream rethrown as unchecked","messagePattern":"Throwable error from subscription stream rethrown as unchecked","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graphql/src/main/java/feign/graphql/GraphqlSubscriptionClient.java","lineNumber":456,"sourceCode":"    private final class PayloadIterator implements Iterator<Map<String, Object>> {\n\n      private final long timeoutMillis;\n\n      private Object pending;\n\n      PayloadIterator(long timeoutMillis) {\n        this.timeoutMillis = timeoutMillis;\n      }\n\n      @SuppressWarnings(\"unchecked\")\n      @Override\n      public boolean hasNext() {\n        if (pending == null) {\n          pending = take();\n        }\n        if (pending instanceof Throwable error) {\n          pending = DONE;\n          throw asUnchecked(error);\n        }\n        return pending != DONE;\n      }\n\n      @SuppressWarnings(\"unchecked\")\n      @Override\n      public Map<String, Object> next() {\n        if (!hasNext()) {\n          throw new NoSuchElementException();\n        }\n        var payload = (Map<String, Object>) pending;\n        pending = null;\n        return payload;\n      }\n\n      private Object take() {\n        try {\n          var event =","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/OpenFeign/feign/blob/e2a1e27560a1e68840c34f031afca88b36096e30/graphql/src/main/java/feign/graphql/GraphqlSubscriptionClient.java#L438-L474","documentation":"In GraphqlSubscriptionClient's iterator, hasNext() pulls the next pending item; if the subscription stream emitted a Throwable, it is rethrown as an unchecked exception (asUnchecked) after resetting pending to DONE. GraphQL subscription errors (server-side execution errors, transport failures) surface to the consumer at iteration time rather than at subscribe time.","triggerScenarios":"Iterating a GraphQL subscription (hasNext()/next() or for-each) after the stream completed with an error: server sent an error payload, the WebSocket/data transport failed, or the client errored during message handling.","commonSituations":"GraphQL resolvers throwing on the server mid-subscription; WebSocket disconnect during a long-lived subscription; authentication expiring while subscribed; invalid subscription query rejected after initial accept.","solutions":["Inspect the rethrown exception (and its cause) for the GraphQL error details from the server.","Wrap subscription iteration in try/catch and implement resubscribe/reconnect logic with backoff.","Fix the subscription query/resolver errors reported by the server payload.","Handle auth token refresh before re-establishing long-lived subscriptions."],"exampleFix":"// before\nwhile (iterator.hasNext()) { process(iterator.next()); }\n// after\ntry {\n  while (iterator.hasNext()) { process(iterator.next()); }\n} catch (RuntimeException e) {\n  log.error(\"subscription failed\", e);\n  resubscribeWithBackoff();\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  while (iterator.hasNext()) {\n    handle(iterator.next());\n  }\n} catch (RuntimeException e) {\n  // stream completed with Throwable; inspect cause and resubscribe\n  resubscribeWithBackoff();\n}","preventionTips":["Always wrap subscription iteration in try/catch and implement automatic resubscribe with backoff","Refresh auth credentials before long-lived subscriptions expire","Log GraphQL server error payloads from the stream","Validate the subscription query against the schema before subscribing"],"tags":["graphql","subscription","reactive-streams","iterator"],"backgroundTag":"upstream-api-error","analyzedSha":"e2a1e27560a1e68840c34f031afca88b36096e30","analyzedAt":"2026-09-10T12:37:37.238Z","contentChangedAt":"2026-09-10T12:37:37.238Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}