github/copilot-sdk · error · IOException

LLM inference response writeResponse() called before…

Error message

LLM inference response writeResponse() called before startResponse()

What it means

This IOException is thrown when a response chunk is written before startResponse() has been called on the LlmInferenceExchange. The exchange enforces a strict lifecycle: startResponse() must initialize the response headers/stream before any chunk can be forwarded over RPC. Writing first would produce a malformed response on the protocol level.

Solutions

  1. Call startResponse() exactly once before the first writeResponseText/writeResponseBinary
  2. Verify all early-return/conditional paths still call startResponse before any chunk write
  3. Centralize response writing in one helper that always invokes startResponse first
  4. If a complete response is already buffered, use the non-streaming response API instead of chunk writes

Example fix

// before
exchange.writeResponseText("hello");
exchange.startResponse();
// after
exchange.startResponse();
exchange.writeResponseText("hello");
Defensive patterns

Strategy: validation

Validate before calling

// track started state in your handler
if (!responseStarted) {
    exchange.startResponse();
    responseStarted = true;
}

Try / catch

try {
    exchange.writeResponseText(chunk);
} catch (IOException e) {
    if (String.valueOf(e.getMessage()).contains("before startResponse")) {
        exchange.startResponse();
        exchange.writeResponseText(chunk);
    } else throw e;
}

Prevention

When it happens

Trigger: Calling writeResponseText or writeResponseBinary (directly or via a streaming writer) without a preceding startResponse() call on the same exchange instance.

Common situations: Refactoring code that constructs a response and moving the write call ahead of startResponse; conditionally calling startResponse (e.g., only when headers are non-empty) but always writing chunks; error paths that skip startResponse then attempt to emit partial output.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/LlmInferenceExchange.java:235

    void errorResponse(String message, String code) throws IOException {
        synchronized (lock) {
            if (finished) {
                return;
            }
            finished = true;
        }
        var error = new LlmInferenceHttpResponseChunkError(message, code);
        var params = new LlmInferenceHttpResponseChunkParams(requestId, "", null, Boolean.TRUE, error);
        join(api().httpResponseChunk(params));
    }

    private void writeChunk(String data, boolean binary) throws IOException {
        synchronized (lock) {
            if (cancelled) {
                throw new IOException("LLM inference request was cancelled by the runtime");
            }
            if (!started) {
                throw new IOException("LLM inference response writeResponse() called before startResponse()");
            }
            if (finished) {
                throw new IOException(
                        "LLM inference response writeResponse() called after endResponse()/errorResponse()");
            }
        }
        var params = new LlmInferenceHttpResponseChunkParams(requestId, data, binary ? Boolean.TRUE : null,
                Boolean.FALSE, null);
        join(api().httpResponseChunk(params));
    }

    private ServerLlmInferenceApi api() throws IOException {
        ServerLlmInferenceApi api = rpcSupplier.get();
        if (api == null) {
            throw new IOException("LLM inference response used after RPC connection closed");
        }
        return api;
    }

View on GitHub (pinned to cd8cf15dc3)