apache/incubator-seata · error · RuntimeException

IO Exception during next()

Error message

IO Exception during next()

What it means

Thrown from SeataHttpWatch.next() when reading a line from the event stream raises an IOException — the connection failed at the transport level (reset, timeout, broken pipe) rather than ending cleanly. The original IOException is attached as the cause for diagnosis.

Solutions

  1. Inspect the cause chain — SocketTimeoutException means readTimeout is too short for a quiet stream; 'connection reset' points to intermediary or peer drops.
  2. Raise okhttp readTimeout to 0 (no timeout) or a value larger than the longest expected gap between events.
  3. Enable TCP keepalive / server-side heartbeats so idle connections are not dropped.
  4. Wrap iteration in a reconnect loop with backoff as streams are inherently failure-prone.

Example fix

// before
OkHttpClient client = new OkHttpClient.Builder()
        .readTimeout(10, TimeUnit.SECONDS) // kills quiet SSE streams
        .build();

// after
OkHttpClient client = new OkHttpClient.Builder()
        .readTimeout(0, TimeUnit.MILLISECONDS) // stream never times out between events
        .retryOnConnectionFailure(true)
        .build();
Defensive patterns

Strategy: retry

Validate before calling

OkHttpClient client = new OkHttpClient.Builder()
        .readTimeout(0, TimeUnit.MILLISECONDS) // event streams must not time out between events
        .retryOnConnectionFailure(true)
        .build();

Try / catch

catch (RuntimeException e) {
    Throwable cause = e.getCause();
    if (cause instanceof java.net.SocketTimeoutException) {
        throw new IllegalStateException("readTimeout too short for watch stream", e);
    }
    scheduleReconnectWithBackoff(); // reset / broken pipe -> reconnect
}

Prevention

When it happens

Trigger: watch.next() with the socket dying mid-read: connection reset by peer, socket read timeout on the OkHttp client, TLS session torn down, or network path loss during event streaming.

Common situations: OkHttp client configured with a short readTimeout that fires on quiet streams; NAT/firewall dropping long-lived idle connections; server or intermediary resetting connections; mobile/unstable networks.

Related errors


AI-assisted analysis of apache/incubator-seata@e01f97c6db (2026-08-14). Data as JSON: /api/errors/53c0186033b702e5. Report an issue: GitHub.

Appendix: source

Thrown at common/src/main/java/org/apache/seata/common/util/SeataHttpWatch.java:177

            Read a single line and parse it as an event.
            Format: "{prefix}{json}\n" where prefix is defined in Constants.WATCH_EVENT_PREFIX.
            Each line is a complete event, event type is included in the JSON data.
            */
            String line = source.readUtf8Line();
            if (line == null) {
                throw new RuntimeException("Stream closed unexpectedly");
            }

            if (!line.startsWith(Constants.WATCH_EVENT_PREFIX)) {
                throw new RuntimeException("Invalid event format: expected prefix '" + Constants.WATCH_EVENT_PREFIX
                        + "', got: " + (line.length() > 20 ? line.substring(0, 20) + "..." : line));
            }

            String jsonData = line.substring(Constants.WATCH_EVENT_PREFIX.length());
            return parseEvent(jsonData);

        } catch (IOException e) {
            throw new RuntimeException("IO Exception during next()", e);
        }
    }

    /**
     * Parse event JSON into Response object.
     * Simplified format: only contains group, timestamp, and metadata fields.
     *
     * @param json the JSON string to parse
     * @return the parsed Response object
     * @throws IOException if parsing fails
     */
    private Response<T> parseEvent(String json) throws IOException {
        try {
            T eventData = JsonCodecFactory.getCodec().parseObject(json, eventType);
            return new Response<>(Response.Type.UPDATE, eventData);

        } catch (Exception e) {
            LOGGER.error("Failed to parse event JSON: {}", json, e);

View on GitHub (pinned to e01f97c6db)