quarkusio/quarkus · error · java.lang.IllegalStateException

Was already open

Error message

Was already open

What it means

SseEventSourceImpl.registerAfterRequest throws this IllegalStateException if the source's isOpen flag is already true when the HTTP client tries to register the SSE listener on the response. Each SSE event source may only open one stream; opening it twice is a lifecycle misuse.

Source

Thrown at independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/SseEventSourceImpl.java:152

                    delayMs = Math.min(serverDelayMs, Math.max(defaultDelayMs, 300_000));
                }
            } catch (NumberFormatException e) {
                // unparseable — use default
            }
        }
        Vertx vertx = webTarget.getRestClient().getVertx();
        if (timerId != -1) {
            vertx.cancelTimer(timerId);
        }
        timerId = vertx.setTimer(delayMs, this);
    }

    /**
     * Allows the HTTP client to register for SSE after it has made the request
     */
    synchronized void registerAfterRequest(HttpClientResponse vertxClientResponse) {
        if (isOpen)
            throw new IllegalStateException("Was already open");
        isOpen = true;
        registerOnClient(vertxClientResponse);
    }

    private void registerOnClient(HttpClientResponse vertxClientResponse) {
        // make sure we get exceptions on the response, like close events, otherwise they
        // will be logged as errors by vertx
        vertxClientResponse.exceptionHandler(t -> {
            if (t == ConnectionBase.CLOSED_EXCEPTION) {
                // we can ignore this one since we registered a closeHandler
            } else {
                receiveThrowable(t);
            }
        });
        // since we registered our exception handler, let's remove the request exception handler
        // that is set in ClientSendRequestHandler
        vertxClientResponse.request().exceptionHandler(null);
        connection = vertxClientResponse.request().connection();

View on GitHub (pinned to e1c734241f)

Solutions

  1. Create a new SseEventSource (target.sse()) for each connection/open call
  2. Close the existing source before attempting to register/open again
  3. Guard calls with source.isOpen() before opening

Example fix

// before
SseEventSource source = sseSource; // reused
source.open(); // second call -> Was already open
// after
SseEventSource source = webTarget.sse(); // fresh instance
source.open();
Defensive patterns

Strategy: try-catch

Validate before calling

if (source.isOpen()) {
    source = webTarget.sse(); // replace with a fresh source
}
source.open();

Type guard

SseEventSource ensureClosed(SseEventSource src) {
    if (src != null && src.isOpen()) src.close();
    return src;
}

Try / catch

try {
    source.open();
} catch (IllegalStateException e) {
    if ("Was already open".equals(e.getMessage())) {
        source.close();
        source = webTarget.sse();
        source.open();
    } else throw e;
}

Prevention

When it happens

Trigger: Calling open()/register() twice on the same SseEventSource instance, or re-using an already-open source for a new request; manual calls to registerAfterRequest after the source is open.

Common situations: Retrying a failed SSE request by calling open() again on the same source instance instead of creating a new one; caching SseEventSource objects and sharing them across requests; resuming a stream after connection drop without recreating the source.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/b5e831fee01c9893. Report an issue: GitHub.