{"record":{"id":"1561cd46f13eeed4","repo":"grpc/grpc-java","slug":"the-message-disappeared-are-you-reading-from-mu","errorCode":null,"errorMessage":"The message disappeared... are you reading from multiple threads?","messagePattern":"The message disappeared\\.\\.\\. are you reading from multiple threads\\?","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"stub/src/main/java/io/grpc/stub/BlockingClientCall.java","lineNumber":129,"sourceCode":"    return read(false, endNanoTime);\n  }\n\n  private RespT read(boolean waitForever, long endNanoTime)\n      throws InterruptedException, TimeoutException, StatusException {\n    Predicate<BlockingClientCall<ReqT, RespT>> predicate = BlockingClientCall::skipWaitingForRead;\n    executor.waitAndDrainWithTimeout(waitForever, endNanoTime, predicate, this);\n    RespT bufferedValue = buffer.poll();\n\n    if (logger.isLoggable(Level.FINER)) {\n      logger.finer(\"Client Blocking read had value:  \" + bufferedValue);\n    }\n\n    CloseState currentCloseState;\n    if (bufferedValue != null) {\n      call.request(1);\n      return bufferedValue;\n    } else if ((currentCloseState = closeState.get()) == null) {\n      throw new IllegalStateException(\n          \"The message disappeared... are you reading from multiple threads?\");\n    } else if (!currentCloseState.status.isOk()) {\n      throw currentCloseState.status.asException(currentCloseState.trailers);\n    } else {\n      return null;\n    }\n  }\n\n  boolean skipWaitingForRead() {\n    return closeState.get() != null || !buffer.isEmpty();\n  }\n\n  /**\n   * Wait for a value to be available from the server. If there is an\n   * available value, return true immediately.  If the stream was closed with Status.OK, return\n   * false.  If the stream was closed with an error status, throw a StatusException. Otherwise, wait\n   * for a value to be available or the stream to be closed.\n   *","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/stub/src/main/java/io/grpc/stub/BlockingClientCall.java#L111-L147","documentation":"BlockingClientCall.read throws IllegalStateException when no buffered value exists and closeState is null, meaning the stream has neither produced a value nor closed. Since read() expects to always see a value or a close, this indicates concurrent reads from multiple threads corrupted the single-consumer contract of this call.","triggerScenarios":"Calling read() concurrently from two threads on the same BlockingClientCall; interleaving read calls such that one thread consumes a value while another checks closeState before it is set.","commonSituations":"Sharing a blocking client call object across worker threads instead of using one call per thread; wrapping read() in a parallel stream or executor without synchronization.","solutions":["Restrict read() calls to a single thread per BlockingClientCall","Synchronize read access externally (e.g. synchronized block or single consumer thread)","Use a non-blocking/async stub with Flowable/StreamObserver if multiple consumers are needed"],"exampleFix":"// before\nExecutorService pool = Executors.newFixedThreadPool(2);\npool.submit(() -> call.read());\npool.submit(() -> call.read()); // races -> IllegalStateException\n// after\nwhile (true) { RespT r = call.read(); if (r == null) break; handle(r); } // single thread","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { RespT v = call.read(); } catch (IllegalStateException e) { // multiple-reader violation: restructure to single consumer }","preventionTips":["One BlockingClientCall per consumer thread","Never share the call across executors/parallel streams","Use async stubs when multiple consumers are required"],"tags":["grpc","thread-safety","concurrency","illegal-state"],"backgroundTag":"invalid-state-transition","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"}