{"record":{"id":"46b770eb69b76531","repo":"grpc/grpc-java","slug":"received-an-unexpected-response-from-a-host-at-the","errorCode":null,"errorMessage":"Received an unexpected response from a host at the S2A's address. The S2A might be unavailable.","messagePattern":"Received an unexpected response from a host at the S2A's address\\. The S2A might be unavailable\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"s2a/src/main/java/io/grpc/s2a/internal/handshaker/S2AStub.java","lineNumber":122,"sourceCode":"   *     writer} calls their {@code onError} method.\n   */\n  @SuppressWarnings(\"CheckReturnValue\")\n  public SessionResp send(SessionReq req) throws IOException, InterruptedException {\n    if (doneWriting && doneReading) {\n      logger.log(Level.INFO, \"Stream to the S2A is closed.\");\n      throw new ConnectionClosedException(\"Stream to the S2A is closed.\");\n    }\n    createWriterIfNull();\n    if (!responses.isEmpty()) {\n      IOException exception = null;\n      try {\n        responses.take().getResultOrThrow();\n      } catch (IOException e) {\n        exception = e;\n      }\n      responses.clear();\n      if (exception != null) {\n        throw new IOException(\n            \"Received an unexpected response from a host at the S2A's address. The S2A might be\"\n                + \" unavailable.\", exception);\n      } else {\n        throw new IOException(\"Received an unexpected response from a host at the S2A's address.\");\n      }\n    }\n    try {\n      writer.onNext(req);\n    } catch (RuntimeException e) {\n      writer.onError(e);\n      responses.add(Result.createWithThrowable(e));\n    }\n    try {\n      return responses.take().getResultOrThrow();\n    } catch (ConnectionClosedException e) {\n      // A ConnectionClosedException is thrown by getResultOrThrow when reader calls its\n      // onCompleted method. The close method is called to also close the writer, and then the\n      // ConnectionClosedException is re-thrown in order to indicate to the caller that send","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/s2a/src/main/java/io/grpc/s2a/internal/handshaker/S2AStub.java#L104-L140","documentation":"S2AStub.send() reads responses from the S2A stream via getResultOrThrow(). If that call raises an IOException, send() wraps it in a new IOException stating an unexpected response was received from a host at the S2A's address, suggesting the S2A might be unavailable. The original IOException is preserved as the cause.","triggerScenarios":"Calling S2AStub.send() and the pending SessionResp future completes exceptionally with an IOException — typically when the endpoint at the S2A's address is not actually the S2A ( wrong host/port ) or the S2A process died mid-stream.","commonSituations":"Misconfigured S2A address pointing at the wrong service; S2A process crashed between request and response; network interruption during a handshake validation.","solutions":["Verify the S2A address/port configuration points at the real S2A process.","Check that the S2A process is running and healthy; restart it if it crashed.","Inspect the cause chain for the underlying IOException and recreate the stub after a stream failure."],"exampleFix":"// before\n// S2A address from stale config\nS2AStub stub = new S2AStub(channelTo(\"localhost:9000\"));\n// after\nS2AStub stub = new S2AStub(channelTo(config.getS2AAddress())); // correct host/port","handlingStrategy":"retry","validationCode":"// probe the S2A endpoint before handshakes\nif (!s2aHealthCheck(address)) {\n  throw new IllegalStateException(\"S2A not reachable/healthy at \" + address);\n}","typeGuard":null,"tryCatchPattern":"try {\n  resp = stub.send(req);\n} catch (IOException e) {\n  if (e.getCause() != null) {\n    logger.warning(\"S2A stream failure: \" + e.getCause());\n  }\n  throw e;\n}","preventionTips":["Validate the S2A address/port configuration at startup.","Monitor S2A process liveness and restart on failure.","Log the cause chain to distinguish wrong-endpoint vs process-crash."],"tags":["grpc","network","s2a","io"],"backgroundTag":"upstream-api-error","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}