{"record":{"id":"d4f382072a2b0db5","repo":"github/copilot-sdk","slug":"the-in-process-runtime-connection-is-closed-d4f382","errorCode":null,"errorMessage":"The in-process runtime connection is closed.","messagePattern":"The in-process runtime connection is closed\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"java/sdk/src/main/java/com/github/copilot/ffi/FfiOutputStream.java","lineNumber":48,"sourceCode":"    @Override\n    public void write(int b) throws IOException {\n        write(new byte[]{(byte) b}, 0, 1);\n    }\n\n    @Override\n    public void write(byte[] b, int off, int len) throws IOException {\n        Objects.requireNonNull(b, \"buffer must not be null\");\n        if (off < 0 || len < 0 || off + len > b.length) {\n            throw new IndexOutOfBoundsException(\"Invalid off/len for buffer of length \" + b.length);\n        }\n        if (len == 0) {\n            return;\n        }\n\n        operationLock.lock();\n        try {\n            if (closing.get()) {\n                throw new IOException(\"The in-process runtime connection is closed.\");\n            }\n            int id = connectionId.get();\n            if (id == 0) {\n                throw new IOException(\"The in-process runtime connection is closed.\");\n            }\n\n            byte[] payload = (off == 0 && len == b.length) ? b : Arrays.copyOfRange(b, off, off + len);\n            if (!nativeBinding.connectionWrite(id, payload, payload.length)) {\n                throw new IOException(\"Failed to write a frame to the in-process runtime connection.\");\n            }\n        } finally {\n            operationLock.unlock();\n        }\n    }\n}\n","sourceCodeStart":30,"sourceCodeEnd":64,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/java/sdk/src/main/java/com/github/copilot/ffi/FfiOutputStream.java#L30-L64","documentation":"FfiOutputStream.write throws this IOException when the stream is closing or already closed (closing flag set under operationLock). Once close() has begun, writes to the in-process runtime connection are rejected to avoid racing the native teardown.","triggerScenarios":"Writing to the stream after close() was called, or concurrently while close() is in progress — the closing AtomicBoolean is observed true inside the write's operationLock section.","commonSituations":"Async writers still flushing a queue when shutdown closes the stream; logging/tracing sinks attached to the stream outliving the host; double-close patterns where a finally block closes but a pending write is still queued.","solutions":["Ensure all writes complete before calling close(); drain writer queues first","Guard writes with an isClosed()/isOpen check and drop or buffer output after shutdown","Synchronize or sequence close() after all producer threads finish (e.g., executor shutdown + awaitTermination)","Catch this IOException in the write path and treat it as a benign shutdown signal"],"exampleFix":"// before\nstream.close();\nstream.write(data);\n// after\nstream.write(data);\nstream.close();","handlingStrategy":"try-catch","validationCode":"// track close in your code; write only while open\nif (streamClosed) return;","typeGuard":null,"tryCatchPattern":"try {\n    stream.write(data);\n} catch (IOException e) {\n    if (String.valueOf(e.getMessage()).contains(\"connection is closed\")) {\n        return; // benign during shutdown\n    }\n    throw e;\n}","preventionTips":["Drain all writers before calling close()","Serialize close after producer threads finish","Do not write from callbacks during shutdown","Treat post-close writes as no-ops in your sink layer"],"tags":["io","ffi","connection","shutdown"],"backgroundTag":"connection-closed","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}