Skip to content

Tolerate Vert.x 5 pause/resume on ended requests (fixes silent test hangs)#5383

Merged
adamw merged 1 commit into
masterfrom
fix/vertx5-pause-resume-after-end
Jul 7, 2026
Merged

Tolerate Vert.x 5 pause/resume on ended requests (fixes silent test hangs)#5383
adamw merged 1 commit into
masterfrom
fix/vertx5-pause-resume-after-end

Conversation

@adamw

@adamw adamw commented Jul 7, 2026

Copy link
Copy Markdown
Member

Problem

The Vert.x 4 → 5 upgrade (80c5e37, 2026-06-10) changed HttpServerRequestImpl.pause()/fetch()/resume() semantics: they now checkEnded() and throw IllegalStateException("Request has already been read") once the request body END event has been delivered. In Vert.x 4 these calls were safe no-ops after end.

Tapir's vertx streaming bridge invokes pause()/resume() from fire-and-forget fibers and Vert.x callbacks:

  • server/vertx-server/cats/src/main/scala/sttp/tapir/server/vertx/cats/streams/fs2.scala — back-pressure loop in a started-and-never-joined fiber
  • server/vertx-server/zio/src/main/scala/sttp/tapir/server/vertx/zio/streams/zio.scala — same pattern, forkDaemon-ed
  • server/vertx-server/src/main/scala/sttp/tapir/server/vertx/streams/Pipe.scala — drain handlers / pipe start
  • server/vertx-server/src/main/scala/sttp/tapir/server/vertx/handlers/package.scalastreamPauseHandler for every streaming/WS endpoint

Under CI load, a pause/resume racing the END event now throws, silently killing the back-pressure fiber (or skipping cleanup such as socket.close()), leaving stream consumers waiting forever. This is the suspected root cause of the intermittent silent hangs in the vertx cats/zio suites seen on dependency PRs #5373 / #5362, where CI went quiet until the 15-minute retry timeout.

Fix

Restore Vert.x 4 semantics at tapir's call sites by ignoring the post-end IllegalStateException: after END there is nothing left to pause/resume, so dropping the call is semantically correct. No fibers/streams were restructured — only the pause/resume calls on the request/read-stream are guarded (.attempt.void in fs2, .ignore in zio, a small ignoringReadEnded helper in Pipe.scala, and a try/catch in streamPauseHandler).

Companion PR: #5382 adds defensive timeouts + a thread-dump watchdog on the CI side; this PR is the root-cause side.

Verification

All variants compile (2.12 / 2.13 / 3): vertxServer{,2_12,3}, vertxServerCats{,2_12,3}, vertxServerZio{,2_12,3}.

Test suites:

  • vertxServerCats2_12/test: Tests: succeeded 292, failed 0 — all passed
  • vertxServerZio3/test: Tests: succeeded 295, failed 0 — all passed
  • vertxServer/test (2.13): Tests: succeeded 554, failed 0 — all passed

Stress run of the formerly racy path — vertxServerCats2_12/testOnly sttp.tapir.server.vertx.cats.CatsVertxServerTest 5 times in a loop: 5/5 passed (285 tests each).

🤖 Generated with Claude Code

Vert.x 5 changed HttpServerRequestImpl.pause()/fetch()/resume() semantics:
they now checkEnded() and throw IllegalStateException("Request has already
been read") once the request body END event has been delivered. In Vert.x 4
these calls were safe no-ops after end.

Tapir's vertx streaming bridge invokes pause()/resume() from fire-and-forget
fibers (fs2/zio back-pressure loops) and from Vert.x callbacks (Pipe drain
handlers, pipe start, streamPauseHandler). Under load, a pause/resume racing
the END event now throws, silently killing the back-pressure fiber or
skipping cleanup (e.g. socket.close()), which leaves stream consumers
waiting forever - the intermittent silent hangs seen in
CatsVertxServerTest / ZioVertxServerTest / WebSocket pipe tests on CI.

This restores the Vert.x 4 semantics at tapir's call sites by ignoring the
post-end IllegalStateException: after END there is nothing left to
pause/resume, so dropping the call is semantically correct.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@adamw adamw merged commit dd5f00b into master Jul 7, 2026
22 checks passed
@adamw adamw deleted the fix/vertx5-pause-resume-after-end branch July 7, 2026 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant