github/copilot-sdk · error · IOException

The in-process runtime connection is closed.

Error message

The in-process runtime connection is closed.

What it means

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.

Solutions

  1. Ensure all writes complete before calling close(); drain writer queues first
  2. Guard writes with an isClosed()/isOpen check and drop or buffer output after shutdown
  3. Synchronize or sequence close() after all producer threads finish (e.g., executor shutdown + awaitTermination)
  4. Catch this IOException in the write path and treat it as a benign shutdown signal

Example fix

// before
stream.close();
stream.write(data);
// after
stream.write(data);
stream.close();
Defensive patterns

Strategy: try-catch

Validate before calling

// track close in your code; write only while open
if (streamClosed) return;

Try / catch

try {
    stream.write(data);
} catch (IOException e) {
    if (String.valueOf(e.getMessage()).contains("connection is closed")) {
        return; // benign during shutdown
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/d4f382072a2b0db5. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/ffi/FfiOutputStream.java:48

    @Override
    public void write(int b) throws IOException {
        write(new byte[]{(byte) b}, 0, 1);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        Objects.requireNonNull(b, "buffer must not be null");
        if (off < 0 || len < 0 || off + len > b.length) {
            throw new IndexOutOfBoundsException("Invalid off/len for buffer of length " + b.length);
        }
        if (len == 0) {
            return;
        }

        operationLock.lock();
        try {
            if (closing.get()) {
                throw new IOException("The in-process runtime connection is closed.");
            }
            int id = connectionId.get();
            if (id == 0) {
                throw new IOException("The in-process runtime connection is closed.");
            }

            byte[] payload = (off == 0 && len == b.length) ? b : Arrays.copyOfRange(b, off, off + len);
            if (!nativeBinding.connectionWrite(id, payload, payload.length)) {
                throw new IOException("Failed to write a frame to the in-process runtime connection.");
            }
        } finally {
            operationLock.unlock();
        }
    }
}

View on GitHub (pinned to cd8cf15dc3)