apache/kafka · error · IOException

Client was shutdown before response was read

Error message

Client was shutdown before response was read

What it means

Thrown as IOException by NetworkClientUtils.sendAndReceive when the polling loop exits because client.active() returned false before a matching response arrived. Unlike the disconnected-response case, here the channel was not flagged disconnected; the blocking call simply observed that the NetworkClient had transitioned out of ACTIVE state (initiateClose/close). It indicates the client was shut down while a blocking request was still outstanding.

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/NetworkClientUtils.java:120

     */
    public static ClientResponse sendAndReceive(KafkaClient client, ClientRequest request, Time time) throws IOException {
        try {
            client.send(request, time.milliseconds());
            while (client.active()) {
                List<ClientResponse> responses = client.poll(Long.MAX_VALUE, time.milliseconds());
                for (ClientResponse response : responses) {
                    if (response.requestHeader().correlationId() == request.correlationId()) {
                        if (response.wasDisconnected()) {
                            throw new IOException("Connection to " + response.destination() + " was disconnected before the response was read");
                        }
                        if (response.versionMismatch() != null) {
                            throw response.versionMismatch();
                        }
                        return response;
                    }
                }
            }
            throw new IOException("Client was shutdown before response was read");
        } catch (DisconnectException e) {
            if (client.active())
                throw e;
            else
                throw new IOException("Client was shutdown before response was read");

        }
    }

    /**
     * Check if the code is disconnected and unavailable for immediate reconnection (i.e. if it is in
     * reconnect backoff window following the disconnect).
     */
    public static boolean isUnavailable(KafkaClient client, Node node, Time time) {
        return client.connectionFailed(node) && client.connectionDelay(node, time.milliseconds()) > 0;
    }

    /**

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Do not close the client from another thread while a blocking call is in flight; sequence shutdown after outstanding calls drain.
  2. Use non-blocking APIs (KafkaProducer.send with Callback, Admin result futures) instead of sendAndReceive where possible.
  3. Catch IOException at the call site and surface it as 'client shutting down' rather than a network error.
  4. Ensure shutdown hooks / bean destroy order waits for in-flight requests before closing the client.

Example fix

// before (blocking call racing with close in another thread)
new Thread(() -> admin.close()).start();
response = NetworkClientUtils.sendAndReceive(client, request, time);
// after (close only after blocking calls drain)
// caller ensures no in-flight blocking call before invoking close()
admin.close(Duration.ofSeconds(10));
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check: if the client is already inactive, fail fast instead of entering sendAndReceive.
if (!client.active()) {
    throw new java.io.IOException(
        "NetworkClient inactive; cannot sendAndReceive. Rebuild the client before retrying.");
}

Type guard

// Treat 'active' as a runtime capability check.
public static boolean canSendAndReceive(KafkaClient client) {
    return client != null && client.active();
}

Try / catch

try {
    return NetworkClientUtils.sendAndReceive(client, request, time);
} catch (java.io.IOException e) {
    if ("Client was shutdown before response was read".equals(e.getMessage())) {
    // The client was closed concurrently. The request is lost. Do NOT retry on this client;
    // build a new NetworkClient and re-issue the request from scratch.
    return reissueOnFreshClient(request);
    }
    throw e;
}

Prevention

When it happens

Trigger: sendAndReceive sends the request, then loops while client.active() is true; the loop terminates with no matching response once active() flips false. This happens when close()/initiateClose() is invoked from another thread during the blocking call. The IOException distinguishes this case from a network-level disconnect.

Common situations: Concurrent close(): one thread blocks in sendAndReceive while another closes the client (e.g. shutdown hook, Spring bean destruction, timeout-driven cleanup); AdminClient.close() racing with an in-flight Admin call; producer.close() during a send callback that itself calls back into the client.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/0ddc90b085b2b1bd.json. Report an issue: GitHub.