{"record":{"id":"0026c2f39c89c5ca","repo":"grpc/grpc-java","slug":"unable-to-perform-write-due-to-unavailable-sink","errorCode":null,"errorMessage":"Unable to perform write due to unavailable sink.","messagePattern":"Unable to perform write due to unavailable sink\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"okhttp/src/main/java/io/grpc/okhttp/AsyncSink.java","lineNumber":230,"sourceCode":"          transportExceptionHandler.onException(e);\n        }\n        try {\n          if (socket != null) {\n            socket.close();\n          }\n        } catch (IOException e) {\n          transportExceptionHandler.onException(e);\n        }\n      }\n    });\n  }\n\n  private abstract class WriteRunnable implements Runnable {\n    @Override\n    public final void run() {\n      try {\n        if (sink == null) {\n          throw new IOException(\"Unable to perform write due to unavailable sink.\");\n        }\n        doRun();\n      } catch (Exception e) {\n        transportExceptionHandler.onException(e);\n      }\n    }\n\n    public abstract void doRun() throws IOException;\n  }\n\n  private class LimitControlFramesWriter extends ForwardingFrameWriter {\n    public LimitControlFramesWriter(FrameWriter delegate) {\n      super(delegate);\n    }\n\n    @Override\n    public void ackSettings(Settings peerSettings) throws IOException {\n      controlFramesInWrite++;","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/okhttp/src/main/java/io/grpc/okhttp/AsyncSink.java#L212-L248","documentation":"AsyncSink throws this IOException from the write thread (WriteRunnable.run) when the underlying okio sink has not been initialized yet, i.e. the transport's start() has not completed or stream creation failed before the sink was set. It signals that gRPC-okhttp cannot write frames because the transport's buffered sink is unavailable.","triggerScenarios":"Calling ClientStream.sendFrame / writing to a stream before the transport finished starting, after a failed transport start, or concurrently with shutdown so the sink field is still null when WriteRunnable runs.","commonSituations":"Servers/clients behind flaky networks where connection setup fails; races between start() and the first write; calling write on a stream whose transport was torn down; bugs in transport lifecycle management.","solutions":["Ensure the transport is started (start(listener)) and its start callback has completed before sending any RPC frames","Check for exceptions reported earlier via transportExceptionHandler — the sink is null because startup failed; fix the root cause","Avoid sharing streams across threads without synchronization; serialize writes through the stream's call executor","Upgrade grpc-okhttp; older versions had races between start and write that could leave sink null"],"exampleFix":"// before\nstream.sendMessage(...); // called immediately after creating transport, before start completes\n// after\ntransport.start(transportListener);\n// wait for listener.transportReady() or use ClientStream listener before writing\nstream.sendMessage(...);","handlingStrategy":"try-catch","validationCode":"if (channel.isShutdown() || channel.isTerminated()) { throw new IllegalStateException(\"channel shut down\"); }","typeGuard":"boolean canSend(ManagedChannel ch) { return !ch.isShutdown() && !ch.isTerminated(); }","tryCatchPattern":"try { stream.sendMessage(msg); } catch (IOException e) { if (e.getMessage().contains(\"unavailable sink\")) { restartTransport(); } else { throw e; } }","preventionTips":["Wait for transportReady/start callback before writing","Never write to streams from multiple threads unsynchronized","Handle transportExceptionHandler to learn why startup failed"],"tags":["grpc","okhttp","io-exception","transport-lifecycle"],"backgroundTag":"internal-invariant-violation","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"}