**Title:** SharedHttpClient can be closed prematurely because its `CloseFuture` is not strongly reachable

**Description:**

When a `SharedHttpClient` is created (via `VertxImpl.createSharedHttpClient`), a `CloseFuture` is instantiated for it. That future is attached to the parent close future so that shutting down the vert.x instance cascades to the shared client. However, the `SharedHttpClient` itself does **not** retain a strong reference to its `CloseFuture`. If the `CloseFuture` becomes weakly reachable and is garbage-collected, it will finalize and close the underlying pooled `HttpClient`, tearing down connections while other components still expect the shared client to be alive.

Observed symptoms include:
- Shared HTTP client connections disappear unexpectedly even though the owning verticle deployments are still active.
- Tests that rely on shared pool lifetime, such as `testSharedPoolClosedAutomatically` and `testSharedPoolRetainedByOtherDeployment` in `SharedHttpClientTest`, exhibit flaky failures because the pool closes too soon.

**Expected behavior:**

`SharedHttpClient` must keep a strong reference to its `CloseFuture` for the entire lifetime of the client. The `CloseFuture` should only be eligible for collection when the `SharedHttpClient` itself is no longer reachable. Additionally, the `close()` / `close(Handler)` methods on `SharedHttpClient` should delegate closing through this retained `CloseFuture` rather than bypassing it, so that the lifecycle is correctly coordinated with the parent close future chain.

**Acceptance criteria:**

1. `SharedHttpClient` retains a strong reference to its `CloseFuture`.
2. Closing the `SharedHttpClient` (with or without a completion handler) triggers the retained `CloseFuture` and properly cascades through the shared-map bookkeeping for the pooled client.
3. Existing shared-client tests continue to pass, and the premature-close race no longer occurs.

**Additional notes:**

- Look at `CloseFuture` semantics: it implements `Closeable` and can be added as a hook. The `SharedHttpClient` should register itself with the `CloseFuture` so that when the future is closed the client’s own `close(Promise)` method is invoked.
- The factory method in `VertxImpl.createSharedHttpClient` currently attaches the future to the parent future but does not pass it to the `SharedHttpClient` constructor; the client needs to receive and store it.

The new shared HTTP client does not keep a reference to its close future, when this close future is finalized it will close the shared client. The shared client must keep a strong reference to its close future.


**Request:** Move HTTP/2 request authority resolution from the headers multi‑map to the request object

As an HTTP/2 server application developer, I need the request authority to be computed correctly and consistently, following HTTP/2 semantics. Currently the logic for resolving the effective authority (falling back from the `:authority` pseudo‑header to the `Host` header) lives inside `Http2HeadersMultiMap`. This has several drawbacks:

* The `Host` header is removed from the header map during authority resolution, which is a side‑effect that should belong to request processing, not to a header container.
* The dual `authority()` / `authority(boolean real)` API on the header class is confusing and leaks implementation details.
* Sanitisation and authority computation are intertwined, making validation and header handling harder to reason about.

**Acceptance criteria**

1. **Effective authority on the request**
   When an HTTP/2 server request is received:
   - If the `:authority` pseudo‑header is present, the request’s `authority()` must return that value.
   - If the `:authority` pseudo‑header is *absent* but a `Host` header is present, the request’s `authority()` must return the value of the `Host` header.
   - After this fallback, the `Host` header must no longer appear in the request’s header map (as required by HTTP/2).

2. **`real` authority**
   `authority(boolean real)` on the request object must return:
   - The original `:authority` pseudo‑header value when `real` is `true` (or `null` if none was sent).
   - The effective authority (same as `authority()`) when `real` is `false`.

3. **Headers multi‑map simplification**
   `Http2HeadersMultiMap` should no longer store or compute a “computed” authority. Remove the fallback from `Host` and the associated side‑effect that deletes the `Host` header. The header object should only retain the raw `:authority` value.
   The existing `validate(boolean isRequest)` method must continue to enforce that when both `:authority` and `Host` are present they match (host and port agree), and that malformed authority values are rejected. However, it must not attempt to create a fallback authority.

4. **Sanitisation call site**
   The call to `sanitize()` on the header multi‑map should be moved from the connection‑level handlers (`Http2ServerConnectionImpl`, `Http2MultiplexServerConnection`) into the request’s header‑processing method (`Http2ServerRequest.handleHeaders`), after the authority fallback has been computed.

5. **Behavioral invariants**
   - A request with only a `Host` header (no `:authority`) must have a non‑`null` `authority()`.
   - A request with both `:authority` and `Host` that disagree must be treated as a protocol error (response reset with `PROTOCOL_ERROR`).
   - For `CONNECT` requests, an authority is mandatory; requests without one must be rejected.
   - The change must not affect the behaviour of `authority()` on `Http2ServerStream` — the stream should continue to delegate to the request, but now the request resolves the value directly rather than through the headers.

The primary visible outcome is that the HTTP/2 server request’s authority methods behave as described, and the internal header multi‑map no longer performs authority fallback or host‑header removal.

Handle authority value computation in Http2ServerRequest instead of the headers implementation.


Issue: HTTPS file transfer times out due to idle timeout handler ordering

When sending a large file over HTTPS, the server may close the connection with an idle timeout before the file transfer completes. This occurs because the idle timeout handler is added to the Netty pipeline before the chunked write handler. During a chunked write, the idle timeout does not receive any write activity signals, causing it to fire while bytes are still being transferred.

The fix should reorder the handlers so that the idle timeout handler (`IdleStateHandler`) is placed after the chunked write handler (`ChunkedWriteHandler`) in the pipeline. This should apply to all connections where the chunked write handler is present, including SSL connections, connections on platforms without `FileRegion` support, and connections with outbound traffic shaping enabled. The idle timeout must still work correctly for connections that do not have a chunked write handler.

Correctly add the idle state handler to a NetSocket when using SSL.


```
Improve connection lifecycle management in the Endpoint to avoid holding stale connection references and to fix a race when connections are created after shutdown.

Currently the Endpoint holds a set of all open connections and closes them when the endpoint is closed. This can leak connections or leave them open if a connection creation completes after the endpoint is already closed. Remove the connection set and instead use a reference counter to decide when the endpoint can be disposed. The disposal condition remains: reference count reaches zero AND no pending requests.

When a connection is about to be added and the endpoint is already closed, the connection must be closed immediately and the request must fail with an error instead of being added silently.

The old close(C) hook that subclasses could override to close a single connection is no longer needed. Remove it; connection closing is now handled by the pool’s eviction callbacks, and the reference counter is decremented when a connection is removed. Subclasses (ClientHttpStreamEndpoint, WebSocketEndpoint) must stop overriding close and instead use the new reference-counting mechanism for add/remove.

The Pool’s connectionAdded/connectionRemoved callbacks currently receive a Lease<C> wrapper. Change them to receive the raw connection C directly, because the pool does not actually need the lease object.

Update tests to match the new lifecycle API, including the removal of the close override and the use of reference counting.
```


**Title:** Simplify `RegistrationStream` by removing `ReadStream` inheritance and adding explicit start/stop lifecycle

**Description:**
The `RegistrationStream` SPI currently extends `ReadStream<List<RegistrationInfo>>`, forcing consumers to implement back‑pressure (`pause`/`resume`/`fetch`) that is unused and semantically wrong for cluster registration updates.
Change the interface so it no longer extends `ReadStream`, and replace the old lifecycle with a simple push‑based listener:

- Introduce `start()` and `stop()` methods to control listening.
- `handler(Handler<List<RegistrationInfo>>)` must be invoked only when the registration list is **not empty**.
- `endHandler` must be invoked when registrations become empty (or if there were never any registrations).
- `exceptionHandler` should fire when the stream is broken; once called, the consumer need not call `stop()`.
- The initial state remains accessible via `initialState()`, but only before `start()` is called; after `start()` the availability is undefined.
- Remove `pause`, `resume`, `fetch`, and `close`.

Update the built‑in delivery strategy (`DefaultDeliveryStrategy`) to use the new contract: call `start()` after wiring handlers, always update the node selector with the received list in the update handler, and tear down the selector when `endHandler` or `exceptionHandler` fires.

RegistrationStream does not need to be a ReadStream


# Bug: HttpClientMetrics.responseBegin sometimes called with null HttpClientResponse

**Problem:**
Implementations of `HttpClientMetrics.responseBegin(HttpClientMetric requestMetric, HttpClientResponse response)` occasionally receive `null` as the `response` argument. This violates the expectations of the metrics API: the callback is meant to signal the start of an HTTP response, and the response object should always be non-null so that metrics can inspect the response status, headers, etc. A null response leads to `NullPointerException` in user code that tries to use the response inside the callback.

**Expected behavior:**
The `responseBegin` method must always be invoked with a non-null, fully initialized `HttpClientResponse` instance. The response must be constructed before the metrics callback is triggered, not after.

**Impact:**
Any custom `HttpClientMetrics` implementation that accesses the `response` parameter (which is part of the intended usage) is prone to crash intermittently. This makes the metrics SPI unreliable and hard to use.

**Fix direction:**
Ensure that wherever `responseBegin` is called, the `HttpClientResponse` object is created and assigned before the callback invocation. The metrics callback should be the last step after the response object is ready, not before.
supervision_value: 2
difficulty: 2
I'll now produce the final answer with the requested structured output (supervision/difficulty + reasoning) and the bug-request text.supervision_value: 2
difficulty: 2
reasoning: The fix is a straightforward reordering of two statements in a single method to avoid passing null to a metrics callback. The training value is limited: it teaches checking a callback’s argument ordering and a simple local edit, but does not require nonlocal reasoning, multiple implementation comparisons, or deep API contract study. Difficulty is easy because the problem is confined to one production file and the fix follows directly from reading the surrounding code.

```text
Bug: HttpClientMetrics.responseBegin sometimes called with null HttpClientResponse

Problem:
Implementations of HttpClientMetrics.responseBegin(HttpClientMetric requestMetric, HttpClientResponse response) occasionally receive null as the response argument. This violates the expected contract of the metrics SPI: the callback signals the start of an HTTP response, and callers expect a non-null response object to inspect status, headers, and other details. Receiving null causes NullPointerException in user code that legitimately uses the response parameter.

Expected behavior:
responseBegin must always be invoked with a valid, fully initialized HttpClientResponse instance. The response object should be constructed and ready before the metrics callback is triggered—never after.

Impact:
Any custom HttpClientMetrics implementation that accesses the response parameter (which is the intended usage) can crash intermittently, making the metrics integration unreliable.

Fix direction:
Ensure that wherever responseBegin is called, the HttpClientResponse is created and assigned before the metrics callback. The callback should be the last step once the response object is available, not before.
```

Avoid to call HttpClientMetrics#responseBegin with a null HttpClientResponse object


```
In VertxHttp2ConnectionHandler, when the HTTP/2 connection becomes inactive
before the initial SETTINGS frame has been received, the pending connect
future is never completed, so client code hangs indefinitely.

The handler must fail the connect future in this scenario, reporting the
connection closure to the caller rather than silently ignoring it. Use the
same "connection closed" exception that the rest of Vert.x core uses for
unexpected disconnects; the fix must not affect the normal close path after
the HTTP/2 handshake has completed.
```

When an HTTP/2 client connects, the failure will not be reported when the server disconnects without sending the initial settings.


Implement clustered registration support in `FakeClusterManager` – `register`, `unregister`, and `registrationListener` currently throw `UnsupportedOperationException`. They need to work so that the fake cluster manager can be used for clustered event-bus tests:

- Maintain an in-memory per-address list of `RegistrationInfo`.
- `register` should add the info and immediately complete.
- `unregister` should remove it and complete successfully, or fail if not found.
- `registrationListener` should return a `RegistrationStream` that captures an initial snapshot and then periodically polls the registration list, emitting updates through the `ReadStream` back-pressure API.
- On `leave`, clean up registrations belonging to the leaving node.

While making this work, two additional issues in clustered event-bus logic need fixing:

1. **`DefaultDeliveryStrategy.NodeSelector.isNodeAccessible`**
   The condition for local-only registrations is inverted:
   `!registrationInfo.isLocalOnly() || !registrationInfo.getNodeInfo().equals(nodeInfo)`
   This makes a local-only registration incorrectly accessible from any node *except* its own. Fix it so that a local-only registration is only accessible when `registrationInfo.getNodeInfo().equals(nodeInfo)`.

2. **`ClusteredEventBus.removeRegistration`**
   If `completionHandler` is `null`, passing `null` to `clusterManager.unregister` can lead to an NPE. Make it safe by logging failures when no handler is provided, rather than passing `null` downward.


**Request: Fix JsonObject.equals to require identical key sets**

The `JsonObject.equals` implementation currently checks only that the two maps have the same size and then compares the values entry-by-entry. This can incorrectly return `true` for objects that have the same number of entries but different keys, when the values involved are all `null`. For example, `{"a": null}` and `{"b": null}` are reported as equal, which violates the expected contract: two `JsonObject`s should be equal only if they have exactly the same keys, with equal values for each key (treating `null` values properly).

Expected behavior:
- `equals` must verify that both objects contain the identical set of keys.
- After confirming key-set equality, it should compare the value for each key, where `null` must be distinguished from a missing key.
- The fix should be minimally invasive and not change the public API or the semantics of value comparison for existing correct cases (e.g., comparing non-null values or objects with truly matching keys).

Acceptance criteria:
- `new JsonObject().put("a", 1)` does NOT equal `new JsonObject().put("b", 1)` (different non-null keys).
- `new JsonObject().putNull("a")` does NOT equal `new JsonObject().put("a", 1)` (null vs non-null).
- `new JsonObject().put("a", 1)` does NOT equal `new JsonObject().putNull("a")`.
- `new JsonObject().putNull("a")` does NOT equal `new JsonObject().putNull("b")` (different keys, both null).
- `new JsonObject().putNull("a")` equals `new JsonObject().putNull("a")`.
- Objects with the same non-null values and identical keys remain equal as before.
- The fix must not break equality of nested `JsonObject` or `JsonArray` values.

JsonObject equality should check the map keys instead of the sizes -closes #3170


## HTTP/2 connection evicted from pool multiple times, corrupting pool state

When an HTTP/2 client connection is terminated with a GOAWAY frame (sent or received), the connection is evicted from the connection pool so that no further requests are multiplexed over it. However, it's possible for the eviction to be triggered more than once for the same connection—for example, when both a `GOAWAY` is sent and later the connection closes, or when a `GOAWAY` is received and then the local endpoint also sends a `GOAWAY`. Each eviction attempt removes the connection from the pool, so a double eviction can leave the pool in a corrupt state: subsequent eviction or acquisition operations might accidentally affect a different connection, leading to connection leaks, "connection already removed" errors, or the pool returning a connection that should have been discarded.

**Desired behavior:**
The eviction of an HTTP/2 connection from the pool must be idempotent. Once a connection has been evicted (on the first `GOAWAY` frame or on connection close), any subsequent eviction attempts for that same connection must be safely ignored, with no effect on the pool state.

**Constraints and guidance:**
- The fix should not alter the normal lifecycle of the connection (graceful shutdown, resource cleanup, etc.); only the pool eviction must be guarded.
- The behavior when no eviction has occurred yet must remain unchanged.
- Ensure that the guarded eviction works correctly in both client-initiated and server-initiated GOAWAY scenarios.
- The change should be minimal and avoid introducing new concurrency issues.

Avoid evicting an HTTP/2 connection from the pool multiple times


**Bug: HttpClientRequest.query() returns stale value after setURI()**

**Observable behavior**:
After calling `setURI(String)` on an `HttpClientRequest`, the `path()` method correctly reflects the new URI, but the `query()` method may still return the query string from the *previous* URI. This violates the expectation that query results are always derived from the current URI.

**Expected invariants**:
1. For any sequence of `setURI(uri)` calls, subsequent `path()` and `query()` calls must return values that correspond to the most recent `uri`.
2. If the URI contains no query component (e.g., `/`, `/foo`, `""`), `query()` must return `null`.
3. If the URI includes a query delimiter but no actual parameters (e.g., `?`), `query()` must return `""` (empty string).
4. `path()` must return the path segment; empty URIs should yield an empty path.

**Additional simplification opportunity**:
The current `path()` implementation contains a redundant empty/check: `uri.length() > 0 ? ... : ""`. The underlying helper `HttpUtils.parsePath` already handles empty input correctly (it never returns null), so that conditional can be removed.

**Acceptance criteria** (non‑exhaustive, to be verified via tests):
- `setURI("/?")` → `path() == "/"`, `query() == ""`
- `setURI("/foo?bar")` → `path() == "/foo"`, `query() == "bar"`
- `setURI("/baz?key=value")` → `path() == "/baz"`, `query() == "key=value"`
- `setURI("")` → `path() == ""`, `query() == null`
- `setURI("?")` → `path() == ""`, `query() == ""`
- The fix must handle the case where `query()` previously returned a non‑null value; after `setURI` with a URI that has no query, `query()` must return `null` and subsequent `query()` calls must *not* re‑parse the URI (i.e., the `null` result is cached).

**Note for the implementer**:
The root cause is that `setURI` invalidates the lazy `path` cache but does **not** invalidate the `query` cache. A simple `query = null` reset is insufficient because `HttpUtils.parseQuery` can return `null`, which would cause repeated re‑parsing. Introduce an invalidation flag so that the `null` result can be cached properly and cleared on every `setURI`.

Fix HttpClientRequest setURI(uri), query(), path()


**Ticket: Pool idle eviction must be driven by the pool's own progress loop, not by external iteration**

*Component:* `Pool` / `ConnectionManager`
*Issue:* #2930 — race conditions and incorrect lifecycle when idle connections are evicted from outside the pool's state machine.

### What's wrong

Today `ConnectionManager.checkExpired()` has a periodic timer that calls `pool.closeIdle(timestamp)`. Internally `closeIdle` walks the available connections directly, evicts them, and closes them — all outside the pool's normal progress machinery (`nextTask` / `checkProgress`). This bypass breaks the pool's invariant: every state-changing action (connecting, recycling, evicting waiters) must be a progress task, so that the pool's internal sequencing and closed-state transitions remain correct.

Concrete problems seen:

* Idle connections that are immediately recycled (TTL = 0) aren't picked up by the progress loop, causing the pool to never reach fully-closed state.
* When there are no pending waiters but idle connections exist, the pool has no way to wake up and evict them unless an external timer prods it — yet the existing external code can race with in-flight recycles/connections.

### Required behaviour

1. **Eviction as a progress task**
   Idle connection eviction must be produced by the pool's own `nextTask()` method, just like acquiring a connection for a waiter or evicting a waiter over the queue limit. A task that closes one expired idle connection should be returned when there is capacity (idle connections) and the connection is expired per the pool's clock. The clock must be injectable (`LongSupplier`) so tests can control time without relying on `System.currentTimeMillis()`.

2. **Progress condition**
   `canProgress()` must return `true` when there are idle connections (capacity > 0) even if there are no waiters. This ensures that `checkProgress()` can enter the task loop and let eviction happen without an external prod.

3. **`closeIdle()` simplification**
   `Pool.closeIdle()` should no longer accept a timestamp; it should just call `checkProgress()` to trigger the pool's self‑management. The periodic timer in `ConnectionManager` will still call `closeIdle()`, but the pool decides *what* to evict based on its internal clock and expiration timestamps.

4. **Lifecycle correctness**
   After the last idle connection is closed through the progress loop, the pool must finish any pending close actions and transition to closed state as usual. Recycling a connection with `timestamp == 0` (expire immediately) should work safely, even inside a waiter's success callback, without leaving the pool stuck open.

5. **No external API breakage**
   The changes are internal to `Pool` and the single callsite in `ConnectionManager`. The public contract of `ConnectionManager` and how client code interacts with it must not change.

### Background / anchors

* Pool's progress loop: `checkProgress()` → loop calling `nextTask()` → execute task → repeat.
* `nextTask()` currently handles: serving a waiter from an available connection, initiating a new connection, or evicting a waiter when the queue is full.
* Idle connections are stored in `available` with an `expirationTimestamp` set during `recycle()`.
* Tests in `ConnectionPoolTest` already exercise pool lifecycle; the fix must pass existing tests plus a new scenario where connections are recycled with TTL=0 inside the success callback, and the pool subsequently closes.

### Acceptance criteria

* An idle connection expired according to the pool's clock is evicted by a task produced from `nextTask()`, not by external iteration.
* The pool correctly reaches closed state when the last idle connection is evicted this way.
* `Pool.closeIdle()` triggers eviction via the progress loop; no direct iteration or connection closing happens there.
* Tests can supply a custom `LongSupplier` so that connection expiration behaviour can be verified deterministically.
* The existing periodic timer in `ConnectionManager` continues to trigger idle cleanup, but purely via the pool's progress mechanism.

ConnectionPool should evict unused connection as a progress task - fixes #2930


```
Title: Support decoding JSON base64 strings into Buffer via JacksonCodec

When using the Jackson-based JSON codec (JacksonCodec), encoding a Buffer
to JSON produces a base64-encoded string (via BufferSerializer). However,
the reverse operation – decoding a JSON string back into a Buffer via
fromString(str, Buffer.class) – is not supported. This is inconsistent
with the existing support for `byte[]`, which can already be decoded from
a base64 JSON string.

We should add support so that a JSON string containing a base64 value can
be decoded into a Buffer, following the same semantics as the existing
byte[] decode path.
```

We should support decoding JSON strings when providing the Buffer.class with plain Jackson


**Acceptance Criteria**

- **Metrics attribution for reply failures**
  When a message send that includes a reply handler fails because no handler is registered at the destination address, the `EventBusMetrics.replyFailure` callback must receive the generated reply address (e.g., `__vertx.reply.1`) rather than the original send address. This ensures that failure metrics are correctly attributed to the reply channel, not the destination.

- **Timeout handling for replies**
  Reply timeouts (configured via `DeliveryOptions.setSendTimeout` / `sendTimeout`) must still be enforced, and a timed‑out reply must result in a `ReplyFailure.TIMEOUT` failure delivered to the reply handler.

- **Separation of concerns**
  The `HandlerRegistration` class (which represents a generic message consumer) must not contain reply‑specific behaviour: it should no longer know about reply timeouts, reply failure messages, or the creation of synthetic failure messages.
  The reply reply‑specific logic (timeout management, failure reporting, and the lifecycle of the temporary reply consumer) must be handled by the event bus implementation itself, without leaking into the generic consumer registration.

- **Behavioural preservation**
  The change must not alter the outward behaviour of message sends, replies, or reply handler invocations except for the metrics address correction described above.
  Temporary reply consumers must still be unregistered automatically when a reply is received or a timeout/failure occurs.

Cleanup : handle reply specific aspects outside of HandlerRegistration


**Summary**: Change `ClusterManager.setNodeInfo` and `getNodeInfo` from returning `Future` to accepting a `Promise` parameter, matching Vert.x’s standard SPI pattern.

---

The `ClusterManager` SPI interface declares two node-info methods that return `io.vertx.core.Future`:

* `Future<Void> setNodeInfo(NodeInfo nodeInfo)`
* `Future<NodeInfo> getNodeInfo(String nodeId)`

This design is inconsistent with the rest of Vert.x’s async SPI contracts (e.g., `join`, `leave`, distributed lock methods), which all accept a `Promise` parameter and let the implementation decide when and on what context to complete it. The return-value-based API forces implementors to construct and return a `Future` directly, which can cause subtle context/threading issues and makes it harder to integrate with non-Vert.x cluster providers.

Replace the return values with `void` and add a `Promise` parameter:

* `void setNodeInfo(NodeInfo nodeInfo, Promise<Void> promise)`
* `void getNodeInfo(String nodeId, Promise<NodeInfo> promise)`

Implementation notes:
* **Callers**: `ClusteredEventBus.start()` and `ConnectionHolder.connect()` currently chain on the returned futures. They must be adapted to create a `Promise`, pass it to the new methods, and then resume composition on `promise.future()`. The async control flow and error handling must be preserved exactly.
* **Unknown node**: `getNodeInfo` for a node id that is not a cluster member must still produce a failure. After the change, that means calling `promise.fail("Not a member of the cluster")` (or an equivalent message) instead of returning a failed future.
* **Test implementation**: `FakeClusterManager` in the test sources implements `ClusterManager` and must be updated to the new signatures. The same success/failure semantics apply: `setNodeInfo` must complete the promise, and `getNodeInfo` must complete or fail the promise based on node-id membership.

No other methods or public APIs are affected. The goal is a minimal, backward-incompatible SPI cleanup that removes the need for implementors to manufacture `Future` instances and realigns the interface with Vert.x’s promise-oriented conventions.

ClusterManager: use promises instead of futures for nodeInfo mgmt


**Bug: Domain socket connect failures (FileNotFoundException) do not trigger reconnect attempts**

**Title:** `NetClient` reconnect does not retry when UNIX domain socket file is missing

**Severity:** Minor

**Description**
When using `NetClient` with `NetClientOptions` that enable reconnection (e.g. `setReconnectAttempts` and `setReconnectInterval`), connection attempts to a UNIX domain socket path that does not yet exist fail immediately and are never retried. The same reconnect logic works correctly for regular TCP connections when the server is not yet listening (which raises a `ConnectException`). This asymmetry makes it impractical to use domain sockets with applications where the server component may start after the client.

**Steps to reproduce**
1. Create a `NetClient` with `NetClientOptions` specifying a reconnect interval and a positive number of reconnect attempts.
2. Attempt to connect to a UNIX domain socket path (e.g. `/tmp/mysocket`) that does not currently exist.
3. Observe that the client throws `FileNotFoundException` (or passes it to the `connect` handler) and stops immediately, never retrying.
4. Start the server after a short delay; the client never reaches it.

**Expected behavior**
The client should obey the configured reconnect policy for domain socket connections just as it does for TCP. When the socket file is absent (causing a `FileNotFoundException`), it should retry the connection until the server creates the file, the number of reconnect attempts is exhausted, or the client is closed.

**Actual behavior**
Only `ConnectException` triggers a retry. A missing domain socket file yields `FileNotFoundException`, which bypasses the reconnect loop and immediately fails the connection attempt.

**Environment**
- Linux with native transport and domain socket support enabled.
- Vert.x core version: [affected versions]

Handle TCP client reconnect on FileNotFoundException for domain sockets


# QUIC server load balancing breaks when servers are added dynamically

## Observed behavior

When using multiple `QuicServer` instances bound to the same socket address (QUIC load balancing via connection ID routing), adding a new server after the initial bind can cause incoming connections to fail or hang. The problem typically manifests when servers are started in a loop on a shared port while a client connects to each in turn: early connections may succeed but later ones fail to complete.

## Expected behavior

All server instances sharing a socket address should correctly receive and handle connections, regardless of the order in which they were bound. The dispatcher must route each new connection to the correct `QuicServer` based on its connection ID, even when the server is registered after the socket is already open.

## Suspected cause

When the QUIC dispatcher initializes a channel for a new connection, it creates a fresh codec builder for the matching server registration. The load-balancing state carried by the connection ID length and the per-connection-ID address generator does not appear to be propagated into that builder. This likely prevents the codec from correctly associating the connection with the intended server instance.

## Acceptance criteria

- Multiple `QuicServer` instances bound to the same address can be added dynamically and all receive connections.
- The fix should ensure that the connection ID length and the connection ID address generator used for load balancing are properly applied when the dispatcher builds a codec for a new channel.


Reimplement `MessageConsumer` message buffering and backpressure using the existing `InboundMessageChannel` utility (`io.vertx.core.internal.concurrent.InboundMessageChannel`), removing the manual demand-based queue. This aligns the event bus consumer with other Vert.x stream implementations and fixes several behavioral bugs.

Required behavior and invariants:

- **No trampolining**: A message sent from the same context must never invoke the consumer handler synchronously. The handler must always be dispatched asynchronously on the next event-loop tick, avoiding reentrancy and stack overflow.

- **Pause / fetch**: `pause()` must immediately stop delivery. `fetch(long)` must resume delivery with proper backpressure. The internal channel will manage the demand; the consumer must delegate to it directly without maintaining a separate demand counter.

- **Max buffered messages**: When the number of buffered messages reaches `maxBufferedMessages` (default 1000), further incoming messages must be discarded. If a discard handler is configured, it must be invoked for each discarded message; otherwise a warning should be logged.

- **Unregistration**: On `unregister()`, any pending buffered messages must be drained. For each message that cannot be delivered, the discard handler (if set) must be called and the event bus metrics `discardMessage` method must be invoked. The `endHandler` must still be triggered.

- **Discard handler semantics**: The discard handler should be called directly (not through a context emit) when a message is discarded, matching the existing behavior for unregister-triggered discards.

- **API compatibility**: `HandlerRegistration.doReceive` must become `void` (no return value) since discard decisions are handled internally. `ReplyHandler` must be updated accordingly. All existing `MessageConsumer` public methods (`pause`, `fetch`, `handler`, `endHandler`, `discardHandler`) must continue to function.

- **Metrics and tracing**: `EventBusMetrics.discardMessage` must still be called for every discarded message. Tracing integration must not regress; any minor timing differences caused by the asynchronous delivery change should be handled without breaking existing tests (use `assertWaitUntil` where necessary for stable results).

- **Thread safety**: The implementation must be safe for use from multiple event loop threads, relying on the `InboundMessageChannel` for its own concurrency control.

Reimplement MessageConsumer with InboundMessageChannel


Issue: `ReadStreamIterator` blocks the Vert.x worker context when waiting for stream data, preventing other tasks scheduled on the same context from running. This can cause a deadlock when an element must be produced by a task on the same context (e.g., on a virtual-thread context).

Desired behavior: When `ReadStreamIterator`’s consumer-progress await (i.e., when `hasNext()` or `next()` blocks) is invoked from within a Vert.x worker executor, it should suspend the current execution and allow other pending context tasks to run before resuming, so that task serialization is respected. In other environments the existing simple await may be kept.

Improve the ReadStreamIterator to handle Vert.x virtual threads properly.


The QUIC server and client do not support setting `SO_REUSEADDR` on the underlying UDP socket, which makes it impossible to quickly restart a server on the same port after a graceful shutdown. Without `SO_REUSEADDR`, bind attempts often fail with "Address already in use" until the kernel timeout expires. This is the same behaviour that already exists for TCP/HTTP net servers through `NetworkOptions.setReuseAddress(true)`.

Expected behaviour:
- Add a `setReuseAddress(boolean)` / `isReuseAddress()` pair to the QUIC endpoint configuration (covering both `QuicServerConfig` and `QuicClientConfig`), defaulting to the established Vert.x default (`true` for reuse).
- When the QUIC endpoint binds its datagram channel, pass the configured reuse-address flag to the Netty bootstrap (`ChannelOption.SO_REUSEADDR`).

This would bring QUIC sockets in line with the rest of the Vert.x network stack, and eliminate the restart delay on busy hosts.

Bind a QUIC endpoint datagram channel with SO_REUSEADDR to avoid TIME_WAIT state


```
Title: Separate task scheduling from context dispatching in ContextInternal

The current internal execution API (`ContextInternal`) and its helper `executeTask`
conflate two different concerns:
- submitting a task according to the context's concurrency model (event-loop vs. worker pool)
- wrapping the execution with Vert.x thread checks, context association, and timing.

This leads to several observable problems:
1. On worker contexts every task goes through `executeTask`, which asserts the current thread
   is a `VertxThread` and wraps execution with `executeStart`/`executeEnd`. The wrapping is
   redundant because the worker thread already has a context set, and it adds non‑removable
   timing overhead. There is no way to schedule a worker task without that overhead (needed
   for benchmarks or internal plumbing).
2. On event-loop contexts there is no explicit way to run a task on the current thread
   *with* the full wrapping, nor an explicit way to run it *without* the wrapping when that
   is desired.
3. The monolithic approach makes it impossible for internal callers to choose the right
   level of wrapping, which leads to unclear semantics and risk of double‑wrapping or
   missing thread checks in the future.

We need to split the execution path into two distinct primitives on `ContextInternal`:

- **Schedule** – submit a task according to the context’s concurrency model, *without* any
  Vert.x‑thread wrapping (no thread check, no context set, no timing).
  * On an event-loop context this means executing the handler directly on the caller thread
    (since the “schedule” is effectively immediate).
  * On a worker context this means submitting the handler to the worker‑pool executor without
    wrapping it.

- **Dispatch** – execute a task directly on the current `VertxThread` with the full wrapping:
  thread‑type check, context set as current, and timing fences (`executeStart`/`executeEnd`).

After the split, the existing `execute` methods on `EventLoopContext` and `WorkerContext`
should use these primitives:
- `EventLoopContext.execute` → dispatch (it already runs on the event‑loop thread).
- `WorkerContext.execute` → schedule via the worker pool, removing the redundant wrapping
  that was previously applied through `executeTask`.

The helper `executeTask` should be removed; its logic (thread check, context set, timing)
moves into the dispatch path. This gives a clean, composable internal API where every
call site can explicitly choose whether it needs full context wrapping or just respectful
scheduling.
```


```
Title: Clustered EventBus delivers remote messages to local-only consumers

When a node in a clustered Vert.x event bus registers both local and
non-local consumers on the same address, messages that originate from a
remote node may be delivered to the local consumers. This violates the
expectation that a `localConsumer` (via `EventBus.localConsumer`) only
receives messages from the local JVM.

The problem occurs because the inbound clustered message path does not
check whether a handler is local-only before delivering. The delivery
logic must be adjusted so that:

- For **point-to-point (send)** operations that arrive from a remote
  node, the round-robin handler selection must skip handlers that are
  marked localOnly. The exception is reply handlers: a reply handler
  should still be eligible even if it was registered as localOnly, as
  the reply is directed to that handler.

- For **publish/subscribe** operations that arrive from a remote node,
  all localOnly handlers must be excluded from the delivery.

The 'localOnly' property is already available on handler registrations
(in `HandlerHolder`). The fix should ensure that remote messages are
never delivered to local-only consumers, while preserving the existing
behaviour for locally-originated messages and for reply handlers.

The change should be implemented in the clustered event bus layer so
that the standard (non-clustered) event bus is unaffected and preserves
its current behaviour.
```

Local consumer should never get a message sent or published remotely (#4350)


**Request: Refactor `ConnectionProvider.connect` to use `Handler<AsyncResult>` instead of `ConnectionListener` callbacks**

Currently the `ConnectionProvider.connect(ConnectionListener, ContextInternal)` contract expects implementations to call `listener.onConnectSuccess(...)` with a set of parameters (connection, concurrency, channel, context, actualWeight) or `listener.onConnectFailure(context, err)`. This mixes initial-connection result handling into the `ConnectionListener`, which also carries ongoing lifecycle callbacks (`onConcurrencyChange`, `onRecycle`, `onEvict`).

We want to decouple these responsibilities:

1. **Change the `connect` signature** so that, in addition to the listener and context, it accepts an `Handler<AsyncResult<T>>` where `T` is a new value type that bundles all the successful-connection information that was previously passed to `onConnectSuccess` (the connection object, concurrency, channel, context, and actual weight).

2. **Simplify `ConnectionListener`** by removing `onConnectSuccess` and `onConnectFailure`. The listener should only carry the ongoing-connection callbacks; it is no longer responsible for reporting the result of a new connection attempt.

3. **Update all `ConnectionProvider` implementations** (the primary one being `HttpChannelConnector`) to complete/fail the `AsyncResult` handler instead of calling the removed listener methods. When an exception escapes `connect`, the handler must be failed; when the connection succeeds, the handler must be completed with the result object containing the same data that `onConnectSuccess` used to receive.

4. **Update `Pool.createConnection`** so that it uses the new `AsyncResult` handler to update pool state and notify the queued waiter, instead of relying on the listener's (now-removed) `onConnectSuccess`/`onConnectFailure` overrides. The existing synchronized state management (capacity, weight, waiter handling, `checkPending`, `checkClose`) must be preserved exactly—only the callback wiring changes.

5. **Update internal tests** that fake/mock `ConnectionProvider` or `FakeConnection` to respect the new `connect` signature and result-reporting mechanism.

No semantics of connection pooling or concurrency management should change; this is purely a refactoring of how the initial connection result is communicated from the provider to the pool.

Change ConnectionProvider#connect method to use an Handler<AsyncResult> idiom instead of notifying the listener with onSuccess/onFailure


**Bug: Shared worker executor leaks thread pool when no longer referenced**

`Vertx.createSharedWorkerExecutor` returns an executor backed by a shared worker pool. When the returned executor goes out of scope and is garbage collected, its backing thread pool and worker threads should be reclaimed. Currently, the executor’s internal reference keeps the pool alive indefinitely, causing a thread leak.

Make the implementation such that once a shared worker executor becomes unreachable, the associated worker pool is closed and its threads are terminated. The fix must not interfere with the normal lifecycle: explicit closure of the executor, and closure of the owning `Vertx` instance, must still work correctly. The executor should not be strongly retained by the internal close mechanism in a way that prevents garbage collection, and finalization of the executor (or a related close guard) should trigger pool cleanup.

Acceptance criteria: create a shared worker executor, use it to run tasks on worker threads, drop all explicit references to the executor, force garbage collection, and observe that the worker threads are no longer alive.

Shared worker executor should use its own CloseFuture, so a collected shared worker executor will close the referenced thread pool resource.


Convert `JsonCodecLoader` into an eagerly-initialized singleton. The class already has a private constructor; drop the public factory method and instead expose a single shared instance that callers can reference directly. The singleton should be initialized from SPI-provided `JsonCodec` implementations exactly as the factory does today.

Additionally, make `encode(Object)` and `decode(Object, Class<T>)` null-safe: passing `null` in either direction should return `null` rather than throwing. `encode` must also pass `null` through to `encodeBuffer` so the chain stays consistent.


Fix `FutureImpl` so that completing a `Promise<Throwable>` with a `Throwable` value is treated as a successful completion, not a failure. Currently, when the result value itself is a `Throwable`, the internal representation mistakenly interprets it as a failure cause. This causes `succeeded()` to return `false`, `onSuccess` listeners not to be called, and `onFailure` listeners to be invoked incorrectly.

The expected behavior: if a `Throwable` is passed to `complete()`, the future must report success (`succeeded() == true`, `cause() == null`, `result()` returns the same `Throwable`), and success handlers must fire, not failure handlers. Standard failure completions (via `fail()`) must still work as before, and the change must not break any case where the result is not a `Throwable`.


```
Enforce a stricter JSON-type contract for `JsonCodec`:

- The `JsonCodec`, `JsonDecoder`, and `JsonEncoder` interfaces must document that the JSON-type parameter is restricted to the broad JSON types: `JsonObject`, `JsonArray`, `String`, `Number`, and `Boolean`.
- `JsonCodecLoader.decode` must stop performing fallback type coercions. When the codec’s `decode` method throws a `ClassCastException` because the supplied JSON value doesn’t match its expected type, the exception must propagate directly rather than being caught and retried with alternative numeric subtypes, `Float`↔`Double` swaps, `String`→`char` extraction, or downcasts through `int`/`long`/`short`.
- All numeric codec implementations (those that previously declared a specific subtype like `Double`, `Integer`, etc. as their JSON-type parameter) must instead declare `Number` and perform the necessary conversion themselves.
- Remove any codec that uses `Character` as its JSON type since `Character` is not an allowed JSON type.
- Update the wrong-type test cases for numeric codecs to use a non-numeric JSON value (e.g., a `String`), so that the tests still verify a `ClassCastException` is thrown when the JSON type is genuinely incompatible.
- Update the codec service-loader registration file to match the removal and type changes.
```

Updated JsonCodecLoader to new restrictions of JsonCodec


Fix HTTP/2 connection metrics to avoid a negative connection count when a connection is closed before the preface is received. Currently, the TCP-level "connected" metric is signaled only when the HTTP/2 connection preface arrives; if the peer closes the connection after the TLS handshake but before the preface (e.g., idle timeout), the "disconnected" metric is signaled without a prior "connected", leading to an incorrect state such as a negative counter. The metrics should be signaled after the SSL handshake completes and before the preface is expected, so that an early close still sees a matching disconnect.

Also, `NetClientOptions.setApplicationLayerProtocols` should return `NetClientOptions` rather than `ClientOptionsBase` to preserve the fluent API.

The HTTP/2 connection metric is registered agains the TCP metrics when the HTTP/2 connection preface is received. When an HTTP/2 connection is closed before the connection preface is received, the TCP metrics is signaled the connection unregistration without a prior connection registration. As side effect his can lead to incorrect state in a metrics implementation, such as a negative connection count.


**Implementation request: Future#await() swallows TimeoutException from failed future**

The zero-argument `Future#await()` currently delegates internally to `Future#await(long, TimeUnit)` with a negative timeout and a null `TimeUnit`, then catches `TimeoutException` under the assumption that no timeout should ever occur. This catch also inadvertently swallows a genuine `TimeoutException` that is the cause of a failed future.

**Required behavior:**
When a `Future` is failed with a `TimeoutException`, calling `await()` must throw that `TimeoutException` (wrapped in a `RuntimeException` if checked) rather than silently dropping it.

**Implementation direction:**
Rewrite the no-arg `await()` so that it no longer delegates to the timed overload, eliminating the catch of `TimeoutException` that masks the failure cause. The waiting logic (thread suspension, `WorkerExecutor` integration for Vert.x worker threads) must be preserved identically to the timed overload, but without any timeout. After the future completes, the result must be handled in the same way as the timed version does when the wait succeeds: return the result on success, throw the failure cause on failure.

Avoid swallowing a TimeoutException on Future#await


## Bug report: WebSocket client pool starves after connection failures

### Description
When the Vert.x WebSocket client is configured with a maximum connection pool size (`WebSocketClientOptions.setMaxConnections`), connection failures (e.g., because the server is unreachable) cause the pool to eventually stop processing new connection requests. The pool’s internal semaphore (inflight connection counter) is not decremented when a connection attempt fails. As a result, once enough failures accumulate to reach the maximum, the counter stays at the limit, and any waiting connection requests remain queued indefinitely without ever being executed.

### Expected behavior
A failed WebSocket connection attempt should release the pool slot it occupied, decrement the inflight connection count, and immediately pick up a pending waiter from the internal wait queue (if any). The client should never permanently block after transient connection errors—the pool must remain responsive and continue to drain its queue even when individual connection attempts fail.

### Steps to reproduce
1. Create a `WebSocketClient` with a small `maxConnections` (e.g., 5).
2. Establish one successful WebSocket connection to a backend server.
3. Shut down the server so that all subsequent connection attempts fail immediately (e.g., connection refused).
4. Submit more connection requests than the allowed maximum (e.g., 15). Many of these will be internally queued because the pool is “full.”
5. Observe that the futures/promises for the queued requests never complete—they hang forever.

### Impact
After any sustained burst of connection errors, the WebSocket client pool becomes completely unusable; no further connections can be established until the client is restarted. This makes the client unsuitable for environments where transient network outages occur.

### Notes
- The pool implementation is essentially a semaphore plus a wait queue, not a traditional connection pool that reuses established connections.
- The fix needs to ensure proper thread-safe handling of the internal inflight counter and the waiter queue when a connection attempt fails, respecting the existing synchronization rules already used for waiters.

The WebSocket client pool should not starve on connect error.


OutboundMessageQueue currently hardcodes a dependency on `io.netty.channel.EventLoop`. This prevents it from being reused in execution contexts that are not backed by a Netty event loop, such as virtual-thread workers or other threading models that Vert.x supports internally. The queue should instead accept `io.vertx.core.internal.EventExecutor`, which is the abstraction used by `ContextInternal` and already works across different threading models.

Specifically:
- The constructor and the stored reference should be `EventExecutor`, not `EventLoop`.
- All thread‑ownership checks must use `EventExecutor.inThread()` rather than `EventLoop.inEventLoop()`.
- Task scheduling (`execute`) must go through `EventExecutor` instead of calling `EventLoop.execute()` directly.
- The `isWritable()` method is not sensitive to the threading model, but it currently lacks `final`; make it `final` to be consistent with other queue methods.

Existing call‑sites that currently pass a Netty `EventLoop` must be updated to supply a suitable `EventExecutor` that preserves the same single‑thread execution guarantees. For call‑sites that create the queue inside a connection that runs on a Netty channel, the executor must still submit tasks to the channel’s event loop even when the current context is not an event‑loop context. The `ContextInternal` API already exposes `executor()`, `eventLoop()`, and `nettyEventLoop()`; callers should use the most appropriate one to obtain an `EventExecutor` that satisfies the queue’s threading contract.

Impact: This change unlocks reuse of `OutboundMessageQueue` outside of `vertx-core` without carrying a Netty‑specific type, and allows connection‑related code to correctly enforce thread‑affinity across all supported threading models.

OutboundMessageQueue should use an EventExecutor instead of an EventLoop


**Title**: RecordParser delimited mode does not emit the final record when the wrapped stream ends without a trailing delimiter

**Component**: `RecordParser` (delimited parsing, wrapped `ReadStream`)

**Severity**: high – silent data loss

**Description**
When a `RecordParser` is set up for delimited parsing and wraps a `ReadStream`, the parser fails to emit the last buffered chunk as a record if the stream ends without a closing delimiter. For example, feeding chunks `"toto\ntit"` and then `"i"` (no trailing `\n`) and immediately ending the stream should produce two records: `"toto"` and `"titi"`. Currently only the first record is delivered; the second line is silently dropped. The same loss occurs when the parser is paused at the time the stream ends.

Additionally, there is no protection against re‑entrant parsing during emission: if a user’s record handler calls back into `handle()` or triggers another parse while the handler is still executing, parsing should defer until the current emission finishes. Without this, handler order and record boundaries become unpredictable.

**Expected behavior**
1. When a wrapped `ReadStream` ends, any data remaining in the parser’s buffer that does not contain the delimiter must be emitted as a final record (respecting demand and pause/resume semantics).
2. Parsing must not be re-entrant. If `handleParsing` (or equivalent entry point) is already executing, subsequent invocations should be skipped until the current parse/emit cycle is complete. The stream end path should still reliably flush the final record and invoke the end handler.

**Acceptance criteria**
- In delimited mode, all four combinations of {paused, not paused} × {final delimiter present, final delimiter absent} deliver the expected records and call the end handler exactly once, even if the final delimiter is missing.
- Calling `RecordParser.handle()` or equivalent during a handler callback does not cause nested re-parsing; the new input is processed only after the current emission completes.
- Backward compatibility: existing fixed-size and delimited scenarios that already work correctly must not regress.

**Hints**
This likely involves changes in the internal parsing loop and the way the stream end notification (via `endHandler`) triggers final processing. The guard against re-entrancy should be a simple boolean flag; the stream-ended state should also be tracked so that the last partial buffer can be treated as a complete record when the stream is finished.

RecordParser delimited parsing does not read the last file line (#2969)


**Add a `close` method to `DnsClient` to release networking resources**

The `DnsClient` currently lacks a lifecycle method to shut it down. Users have no way to release the underlying datagram channel and stop outstanding DNS queries.

Add a `close()` method to the `DnsClient` interface. The implementation must:

1. Close the datagram channel and release its resources.
2. Fail every in-flight query (queries that have been sent but not yet completed) with a “closed” exception.
3. Return a `Future<Void>` that completes once the channel is closed and all queries have been failed.
4. Be idempotent: calling `close()` multiple times must return the same future and not attempt to close resources twice.
5. Prevent new lookups after `close()` is called: any subsequent call to a lookup method (`resolveA`, `resolveAAAA`, …) must immediately return a failed future with an appropriate “closed” failure.
6. Execute the close operation on the client’s bound event loop to avoid concurrent access to the query map and channel. The method itself may be called from any thread.

The `DnsClient` is already documented as thread-safe and tied to a single event loop. Make sure the close behavior aligns with that guarantee.

The DnsClient does not have a close method to release the underlying networking resources.


**Title:** Fix redundant promise completions and clarify `MessageProducer` write future semantics

**Background**
When using a publish-style `MessageProducer` (created via `EventBus.publisher`), the future returned by `write()` can trigger confusing “already completed” exceptions in application logs. This happens because the internal delivery promise may be completed or failed multiple times, particularly in clustered scenarios.

**Expected behavior**
1. The `MessageProducer.write()` future completion must depend on the producer type:
   - **send / request:** the future succeeds once the message is written, fails if writing fails.
   - **publish:** the future fails **only** when there are no registered handlers for the address at the time of publishing; otherwise it succeeds.
   In all cases a successful future is **not** a delivery guarantee.
2. The underlying promise must be protected against redundant completion/failure (e.g., using `tryComplete`/`tryFail`) so that no “already completed” exceptions are surfaced.
3. The `MessageProducer` Javadoc should be updated to make this contract explicit (currently it vaguely mentions that the future is not a delivery guarantee, without distinguishing publish from send/request).

**Implementation notes**
- The change should affect the internal delivery context’s completion notification and the logic in `EventBusImpl` that resolves the write future.
- For publish, treat any failure **other than** a “no recipients” error (`ReplyException` with `NO_HANDLERS`) as a success in the returned future.
- Ensure the test coverage verifies the clustered publish flow where handlers are registered and unregistered before the write.

Clarify MessageProducer write future behavior (#4935)


```
When compression is enabled via `HttpServerOptions.setCompressionSupported(true)` and a compression size threshold is configured via `setCompressionContentSizeThreshold(int)`, the threshold is effectively ignored for HTTP/1.x connections: all responses are compressed regardless of their body size.

The expected behavior is that responses where the response payload size in bytes exceeds the configured threshold should be compressed, and responses at or below the threshold should be sent uncompressed. This should be implemented for HTTP/1.1 response bodies.

Additionally, update the Javadoc of `setCompressionContentSizeThreshold` to clarify that this threshold is only applicable for HTTP/1.x response bodies.
```


Fix demand tracking in `JsonParser` event mode to respect the `ReadStream<JsonEvent>` backpressure contract. Currently, when the parser is used with object/array *event* mode, calling `pause()` followed by `fetch(n)` does not correctly limit the number of emitted `JsonEvent` items; demand is never decremented for events delivered via the event handler. The result is that `fetch(1)` may deliver zero, one, or more than one event, instead of delivering exactly one event and then automatically pausing.

In addition, when parsing an object in event mode, the field name for a `VALUE` event is sometimes `null` even though it was present in the JSON. This appears to be caused by the field name being cleared before the event is constructed.

Finally, when a parse error occurs and no exception handler is set, the `DecodeException` that is thrown should wrap the original underlying exception as its cause (making stack traces and debugging easier).

The fix should ensure:

- `JsonParser` as a `ReadStream<JsonEvent>` decrements outstanding demand exactly once per `JsonEvent` delivered to the user handler, not per internal token.
- After a `fetch(1)` following a pause, exactly one event is emitted (if available), and no further events are delivered until more demand is requested.
- The `VALUE` event for an object field always carries the correct field name.
- `DecodeException` thrown for unhandled parse errors includes the original `Exception` as its `cause`.

JsonParser does not consume properly the stream demand - closes #3076


```markdown
## DeliveryStrategy initialization callback fires too early in clustered event bus startup

The `DeliveryStrategy` interface defines a `setVertx(Vertx)` callback that is meant to give a
custom delivery strategy access to the Vert.x instance for setup. Its Javadoc currently says:
“Invoked after the cluster manager has started.” However, when using a clustered event bus this
method is called before the event bus is fully started — specifically, before the bus has
completed its own startup sequence.

This timing is incorrect for strategies that need to read cluster metadata (e.g. node info,
rack-ids) that only becomes available after the event bus itself is started. A strategy that
accesses `ClusterManager.getNodeInfo()` inside `setVertx` will observe incomplete or
uninitialized node data, leading to failures.

### Requirements
- Ensure that the delivery strategy’s initialization callback is invoked **after** the clustered
  event bus has started successfully.
- The call must happen before any messages are delivered, so the strategy is ready when the
  first send occurs.
- If the event bus fails to start, the initialization should not be called.
- Update the Java interface and its documentation to reflect the corrected lifecycle: rename the
  method to `init(Vertx vertx)` and adjust the Javadoc to state that it is invoked after the
  clustered event bus has started (not after the cluster manager).
- Update the default implementation (`DefaultDeliveryStrategy`) and any other existing
  implementations to use the new method name and timing.
- The change must preserve backward compatibility at runtime for `DeliveryStrategy`
  implementations that do not rely on any post-start cluster state; their init logic should
  still work correctly.
```

DeliveryStrategy init must be invoked after ClusteredEventBus has started


```
Title: CompositeFuture.toString() causes StackOverflowError

When a completed CompositeFuture (e.g., returned by `CompositeFuture.all(…)`) is converted to a string via `toString()`, the call enters infinite recursion and throws a `StackOverflowError`. This makes it impossible to safely log or inspect composite futures during debugging.

The expected behavior is that `toString()` returns a descriptive representation of the composite future without recursion. Ideally the string should reflect the results or state of the individual futures that were composed, so that diagnostic output is useful rather than crashing the process.
```

The current implementation of the toString() method FutureImpl leads to an infinite reccursion in the CompositeFuture implementation that inherits this method because the future is completed with the CompositeFuture itself.


Write a brief, concrete implementation request as a maintainer's backlog ticket.

Signal the VertxMetrics when the Vertx instance is initialised - fixes #2695


Refactor reply failure delivery in the event bus:
- When a reply times out or has no handlers, deliver the failure as a regular message with a `ReplyException` body through the registered reply handler, instead of directly invoking the `AsyncResult` handler. This ensures the failure passes through interceptors and the handler's context consistently with successful replies.
- Remove direct `metrics.replyFailure` calls for those cases; the failure should be surfaced via the message path.

Fix clustered event-bus metrics:
- When a message is published to both local and remote subscribers, invoke `EventBusMetrics.messageSent` separately for each local and remote delivery, rather than reporting a single combined event. This reflects individual deliveries.

Guarantee ordering in clustered mode:
- Ensure that when a message is sent from a thread that has no Vert.x context, the operation is dispatched on a well-defined context to maintain message ordering guarantees.


**Title**: JsonArray.add(…) should accept `null` as a valid element

**Background**
JSON arrays can contain `null` values, but `JsonArray` currently throws `NullPointerException` when any of its `add(…)` overloads is called with a `null` argument. This prevents callers from building arrays that represent legitimate JSON `[null, ...]` structures.

**Request**
Change all `add` overloads so that passing `null` is accepted and stored as a JSON null element, instead of throwing. The change must cover every public `add` signature:

- `add(String)`, `add(Integer)`, `add(Long)`, `add(Float)`, `add(Double)`, `add(Boolean)`,
- `add(JsonObject)`, `add(JsonArray)`,
- `add(Enum)`, `add(CharSequence)`, `add(byte[])`, `add(Instant)`,
- `add(Object)`.

**Important edge case – transformation-based overloads**
Several overloads transform the argument before storing it (e.g. `Enum` → `.name()`, `CharSequence` → `.toString()`, `byte[]` → Base64 string, `Instant` → ISO-8601 format). When the argument is `null`, those overloads must **not** attempt the transformation; they should simply store a Java `null` in the underlying list. The same rule applies to `add(Object)`, which currently delegates to `Json.checkAndCopy` – that delegation should be skipped for a `null` input.

**Non-goal**
`addAll(JsonArray)` behavior is outside the scope of this ticket (no change requested for that method).

JsonArray add methods should allow the null argument - fixes #2880


The SSL context-update logic in `NetServerImpl` is duplicated across initial server bind (SSL path) and the `updateSSLOptions` method, and the two paths need to be kept consistent. Extract the serialization, `ClientAuth` defaulting, copy/force handling, and `SslContextProvider` storage into a single reusable class. Use this new component in both the bind-time SSL initialisation and the dynamic `updateSSLOptions` call, preserving existing behaviour (updates are serialised, `Future<Boolean>` signals whether the provider actually changed, `force` is passed through). The class should expose the current provider and an `update` method accepting `ServerSSLOptions`, context, and force flag.

Encapsulate server SSL options update logic in a single class for reuse purpose.


**[Keyboard Responder] Click events following prevented keydowns should also be prevented**

**Context**
The Keyboard responder (`packages/react-events/src/dom/Keyboard.js`) exposes a `preventKeys` prop that calls `preventDefault()` on native keydown events matching specified keys. However, many user agents fire a native `click` event immediately after certain keydown actions (e.g., pressing Enter or Space on a button). Currently only the keydown itself is prevented; the subsequent click’s default behavior still executes, defeating the intent of `preventKeys` for those keys.

**Desired behavior**
1. When a keydown event matches a `preventKeys` entry and has its default prevented, any click event on the same target that is part of the same keyboard interaction must also have its default prevented.
2. The prevention should only happen while the keyboard interaction is “active” (i.e., between keydown and keyup). A standalone click without a preceding matching keydown must not be affected.
3. The Keyboard responder’s synthetic events must be easily distinguishable:
   - The `type` field should use the namespace `'keyboard:keydown'` / `'keyboard:keyup'`.
   - The event object should include `pointerType: 'keyboard'` and `isComposing` (mirroring the native event property).

**Concrete requirements**
- Track whether the current interaction has had a keydown default prevented, and whether the keyboard interaction is active (keydown occurred but keyup has not yet fired).
- Add the `'click_active'` target event type so the responder can intercept clicks that originate from a keyboard.
- In the click handler, if the interaction is active and the keydown was default prevented, call `preventDefault()` on the native click event.
- The synthetic `KeyboardEvent` type must reflect the new type strings and include `pointerType`, `isComposing`, and the existing `defaultPrevented` flag.

**Non-goals**
This does not change how `onKeyDown`/`onKeyUp` propagation works; it only extends the native default prevention to cover synthetic click events that follow a prevented keydown.

[react-events] Keyboard calls preventDefault on 'click' events (#16779)


Add a `maximumDistance` prop to the Tap responder configuration. When set to a number ≥ 10, the tap gesture is cancelled if the pointer moves more than that many pixels from the initial contact point (Euclidean distance). Values < 10 are ignored, so existing behavior for tiny movements is unchanged. The dismissal happens during active pointer moves, immediately calling `onTapCancel` and ceasing any further `onTapUpdate` dispatches. This check is performed only while the pointer remains inside the responder target (leaving the target already triggers cancellation).

[react-events] Tap: add maximumDistance prop (#16689)


**Title:** Allow event responders to opt into receiving target events across portal boundaries

**Background**
The Event Responder system (the “Flare” API behind `React.unstable_createResponder`) currently only fires target events when the event target’s fiber is inside the same React tree as the responder listener. If the target is rendered into a different React root via a portal (i.e., a `HostPortal` fiber exists in the hierarchy), responder instances above the portal do not receive the event. This deliberate isolation prevents responders from accidentally leaking across portal boundaries.

However, some built-in and custom responders need to see events from portal targets. For example:
- `Focus` and `FocusWithin` responders need to track focus changes even when focus moves to an element inside a portal (e.g., a modal or tooltip rendered elsewhere).
- `Keyboard` responders should react to key events from any part of the application, including portal content.

Today there is no way to express this preference; the logic in `traverseAndHandleEventResponderInstances` simply does not visit responders above a `HostPortal` fiber when the target is inside it.

**Desired behavior**
Add a new boolean configuration property, `targetPortalPropagation`, to the responder configuration object used with `React.unstable_createResponder`. It defaults to `false`, preserving the existing isolation.

- When `targetPortalPropagation: true` is set, the responder **should** receive target events even if the event target is inside a portal relative to the responder’s listener.
- The traversal logic in `DOMEventResponderSystem.js` should be updated to track whether the walk has entered a portal (by checking the fiber’s `tag` against `HostPortal`). For each responder instance encountered, event delivery should be allowed if the responder explicitly opts in, in addition to the existing checks.

**API sketch**
- Update the flow type `ReactEventResponder` in `shared/ReactTypes.js` to include the new field: `targetPortalPropagation: boolean`.
- Update `createEventResponder.js` to extract the value from the config and default to `false` if missing.
- Update the responder implementations for `Focus`, `FocusWithin`, and `Keyboard` (in `react-interactions/events/src/dom/`) to set `targetPortalPropagation: true` in their configuration objects, so they behave correctly across portals by default.

**Implementation constraints**
- The change must be purely additive and backwards-compatible; existing responders without the flag continue to ignore portal boundaries.
- The check should happen during the fiber traversal in `traverseAndHandleEventResponderInstances`. When a `HostPortal` fiber is encountered, set a local flag `insidePortal` to `true`. Then, when evaluating a responder instance, gate the visit on `!insidePortal || responder.targetPortalPropagation`.
- Import `HostPortal` from `shared/ReactWorkTags` in that module.
- No other parts of the event system should be affected.

**Edge cases**
- Nested portals: the flag should remain true as the traversal continues deeper, so a responder above multiple portal layers still receives the event if opted in.
- Responders without `targetPortalPropagation` should see no change in behavior; they are simply skipped when `insidePortal` is true.
- The flag has no effect on root events (which are dispatched globally anyway).

[react-interactions] Add Portal propagation configuration (#16889)


**Feature: Support hook name parsing when source maps omit column numbers (e.g., `cheap-module-source-map`)**

## Background
In Create React App’s development mode, the default source map type is `cheap-module-source-map`, generated for speed. These source maps intentionally do not contain column numbers. Currently, React DevTools’ hook name parsing fails to resolve names for hooks when column numbers are missing or zero, which leads to unnamed hooks in the DevTools UI.

## Desired behavior
- When DevTools attempts to match an AST node to a hook’s source location, and the source map provides **no column information** (column is `0` or absent), the matching should fall back to **line‑only matching**.
- A match via line‑only is accepted **only if exactly one potential hook declaration exists on that line**. If multiple hook declarations share the same line, no name should be assigned (return `null`).
- If column information is available, the existing precise matching (both line and column) must continue to work as before.

## Acceptance criteria
1. **Single hook per line**: Given a component with a single hook call on a line (e.g., `const [count] = useState(0)`), and a “cheap” source map (no column info), DevTools must correctly resolve the hook’s name.
2. **Multiple hooks per line**: If multiple hooks exist on the same line (e.g., `const a = useA(), b = useB()`), DevTools must not guess; it should leave all hooks on that line unnamed.
3. **Column‑available mode unaffected**: The change must not alter behavior for source maps that include column numbers (e.g., `source-map` or inline source maps). Those should continue to match hooks with full precision.
4. **Non‑column edge cases**: When column is explicitly `null` or `0`, the line‑only logic should be triggered; any other column value should use the existing precise check.

## Implementation constraints
- The logic should be contained in the existing hook‑name parsing pipeline (`astUtils.js`, `parseHookNames.js`), specifically within `checkNodeLocation` and `getHookName`.
- No external API changes; the feature should work transparently when the DevTools extension is connected to a page using `cheap-module-source-map`.

DevTools: Named hooks supports "cheap-module-source-map" (#21874)


**Title:** DevTools should display "SuspenseList" for SuspenseList elements and handle invalid element types gracefully

**Context:**
`getDisplayNameForReactElement` in `react-devtools-shared/src/utils.js` resolves the display name for a React element. It already maps several special built-in types (e.g., `StrictMode`, `Suspense`) to human-readable names. However, the new `SuspenseList` type is missing from this mapping, causing it to fall through to the generic type-name resolution. Additionally, that generic resolution calls `getDisplayName` on any non‑string, non‑null `element.type`, even when that value is not a function (e.g., a symbol, a boolean). This can lead to confusing results or potential runtime issues when inspecting elements with unusual types.

**Desired behavior:**

1. Add support for the `SuspenseList` built‑in type so that `<SuspenseList>` elements appear as `"SuspenseList"` in the DevTools element tree. This should align with how other built‑ins like `StrictMode` and `Suspense` are handled.
2. Tighten the fallback logic in `getDisplayNameForReactElement` so that:
   - If `element.type` is a function, call `getDisplayName(type, 'Anonymous')` (current behavior).
   - If `element.type` is *not* a function but is truthy (e.g., boolean `true`, a `Symbol(…)`, or any other non‑string, non‑function value), return the string `'NotImplementedInDevtools'` instead of attempting to extract a display name.
   - If `element.type` is `null` or `undefined` (element created without a type), return `'Element'` (current behavior).
3. The `typeOf` utility in `react-is` should recognize `SuspenseList` as a React‑element type so that downstream helpers and filters can treat it consistently.

**Non‑obvious edge cases & constraints:**

- The change must not alter the behavior for existing recognized types (`Fragment`, `Profiler`, `StrictMode`, `Suspense`, etc.).
- The `getDisplayName` helper should never be called with a non‑function argument; the new guard prevents that.
- When `element.type` is a string (host component type like `'div'`), the existing early return `return type` must still apply.
- The `typeOf` export in `react-is` is used elsewhere; adding `REACT_SUSPENSE_LIST_TYPE` to its switch statement ensures that `Fiber` reconciliation and test utilities treat it as a known type.


**Title:** React DevTools shows incorrect root type for `ReactDOM.render` and `ReactDOM.hydrateRoot`

**Problem:**
When inspecting the root element of a React application in the React DevTools “Components” panel, the “root type” field displays inaccurate values. For example:

- Applications mounted with `ReactDOM.render(...)` display the root type as `"createLegacyRoot()"`.
- Applications mounted with `ReactDOM.hydrateRoot(...)` display the root type as `"createRoot()"`.

This makes it difficult for developers to quickly understand how the app is bootstrapped, especially when multiple roots with different strategies coexist.

**Steps to Reproduce:**
1. Mount a React app using `ReactDOM.render(<App />, container)`.
2. Open React DevTools and select the root element in the Components tree.
3. Observe the displayed “root type” property.
4. Alternatively, mount an app using `ReactDOM.hydrateRoot(container).render(<App />)` and inspect the root type.

**Expected Behavior:**
The root type should reflect the actual public API used to create the root:
- `ReactDOM.render` → `"render()"`
- `ReactDOM.hydrate` → `"hydrate()"`
- `ReactDOM.createRoot` → `"createRoot()"`
- `ReactDOM.hydrateRoot` → `"hydrateRoot()"`

**Actual Behavior:**
- Legacy roots always display `"createLegacyRoot()"`, regardless of whether `render` or `hydrate` was used.
- Concurrent roots always display `"createRoot()"`, even when the root was created via `hydrateRoot`.

**Impact:**
Developers rely on this metadata for debugging and profiling. The current behavior masks the actual entry point API, which can lead to confusion during migration (e.g., from legacy to concurrent roots) and misdiagnosis of hydration issues. Additionally, `createLegacyRoot` is not a public API and should not appear in user-facing diagnostics.

**Additional Context:**
The root type string is set in the `FiberRootNode` constructor via the `_debugRootType` property and surfaced through the DevTools backend’s inspected element payload. The fix should ensure the correct API name is selected based on the root’s tag (legacy vs. concurrent) and whether hydration was requested.

devtools: Display actual ReactDOM API name in root type (#22363)


**Title:** Capture phase handlers fire too many times for non-bubbling events

**Background**
React delegates most event types at the root, but for events that don’t bubble (e.g. `play`, `scroll`, and other media events) it attaches native listeners directly to each DOM element. In the capture phase, the existing listener-accumulation logic still walks up the fiber tree from the target, collecting `on<Event>Capture` handlers from every ancestor. Because a native capture listener is also attached to each ancestor and fires separately, this leads to those capture handlers being invoked multiple times for a single native event.

**Desired behavior**
When processing a capture-phase event that uses the non-delegated listener path (`IS_NON_DELEGATED`), the system must only accumulate the capture handler for the fiber that owns the native listener that just fired. It must **not** walk up to ancestors — the native capture propagation already ensures each ancestor’s handler will fire in the correct order when its own native listener executes.

**Acceptance criteria**
- For a non-bubbling event fired during the capture phase on a deeply nested element, each `on<Event>Capture` handler in the ancestor chain is called **exactly once**, matching the native capture propagation order.
- The fix must not affect the behavior of delegating (bubbling) events, nor the bubble phase of non-delegated events.
- The change should be localized to the capture-phase accumulation step that decides how far up the fiber tree to traverse.

**Relevant flags / anchors**
- `IS_NON_DELEGATED` and `IS_CAPTURE_PHASE` in `EventSystemFlags`.
- `accumulateSinglePhaseListeners` in `DOMPluginEventSystem.js`.
- The `extractEvents` function in `SimpleEventPlugin.js` where the accumulation strategy is selected.

Fix issue with capture phase non-bubbling events (#19452)


**Title:** Errors thrown inside hidden `<Activity>` boundaries must not escape to visible error boundaries

**Severity:** bug  **Component:** reconciler / Activity (Offscreen)
**Affects:** apps that use `<Activity mode="hidden">` with error-prone content
**Related:** #35073

### Context
`<Activity>` lets you hide and preserve a UI tree. When mode is `"hidden"`, the tree is conceptually offscreen and should behave like an unmounted tree for the purposes of error handling – no error thrown inside it should be observable by ancestor error boundaries in the visible UI. Currently, a thrown exception during the “prerender” of a hidden Activity can escape outward and be captured by an ancestor error boundary, breaking the isolation guarantee. This is inconsistent with how **suspension** is handled inside a hidden Activity, where the work is abandoned and the boundary is simply marked for retry.

### Desired behavior
- While an `<Activity mode="hidden">` boundary is hidden, **any error thrown during rendering** of its subtree is contained. It must not propagate to the parent fiber’s `throwException` path and must not invoke ancestor error boundary lifecycle methods.
- The hidden boundary should be marked so that if the mode later changes to `"visible"`, the render is re-attempted and the error can then surface normally (and be caught by the nearest error boundary, if any).
- If the hidden boundary already has a captured error from a previous hidden-era throw, that error should not be reused prematurely; a fresh render attempt should occur when the boundary becomes visible.

### Implementation notes (for the reconciler)
- Handling should be located in the error-throwing logic (likely `throwException`). When the work-in-progress fiber tag is `OffscreenComponent` (the internal type backing Activity) and its `memoizedState` indicates the boundary is currently hidden, we should **not** search further up the tree. Instead, set the `ShouldCapture` flag on the fiber and return early, letting the render abandon this subtree without surfacing the error.
- This mirrors the existing path for a thrown thenable (suspense) inside a hidden Offscreen boundary: abort the render without triggering a real capture and without calling `getDerivedStateFromError` / `componentDidCatch`.
- No changes needed to the public API of `<Activity>` or `<ErrorBoundary>`.

### Acceptance criteria
- Render a hidden Activity that contains a component which throws. No ancestor error boundary (even if it wraps the Activity) observes the error while the Activity remains hidden.
- When the same Activity is switched to `mode="visible"`, the throw occurs again and the nearest error boundary can capture it normally.
- The hidden boundary’s subtree should be re-rendered when visibility changes, not permanently stuck in an error state.

Fix: Errors should not escape a hidden Activity (#35074)


We need callbacks on Fizz streaming APIs so that developers can hook into key server-rendering lifecycle events:

- **`onError(error)`** – called whenever an error is thrown anywhere in the React tree, even if the error is handled by a Suspense boundary and does not crash the stream. Useful for logging, monitoring, and setting HTTP status codes.
- **`onReadyToStream()`** – called once the root suspense boundary’s fallback (or the root content itself if there is no boundary) has finished rendering and is ready to be flushed. This allows a server to start writing the HTTP response headers and the initial HTML chunk, possibly with custom prefix content, without waiting for the entire tree to resolve. If the user already started writing immediately this callback is typically not needed.
- **`onCompleteAll()`** – called when **all** pending server work is done (the entire tree has resolved, errored, or aborted), regardless of whether the stream has already flushed everything. This lets a consumer buffer the output and send a single complete HTML response, or perform cleanup/analytics when rendering finishes.

These callbacks must be available as optional fields in the `options` object for both `renderToReadableStream` (browser) and `pipeToNodeWritable` (Node), and for the noop renderer used in tests.

Important invariants / behaviour:
- `onError` must be called for every error thrown during rendering, even for errors that are caught inside `<Suspense>` and result in client rendering. It should not replace the stream’s fatal error behaviour.
- `onReadyToStream` must fire exactly once, when the root boundary becomes ready (i.e., `pendingRootWork` reaches 0). At this point the stream can be started without seeing a blank page.
- `onCompleteAll` must fire exactly once, after **all** pending work reaches zero (`allPendingWork === 0`). It must be called at the very end so that callers can synchronously write any final output (e.g., closing HTML tags) if they have buffered. The callback must also fire if rendering was aborted or everything errored.

[Fizz] Expose callbacks in options for when various stages of the content is done (#21056)


In React Native, event handler callbacks do not have access to a global `event` variable that references the event being dispatched. On the web, the event object is exposed globally (`window.event`) during listener invocation, which is a well-established convention relied upon by many patterns and libraries. In React Native, however, inside an `onPress`, `onTouchStart`, or similar handler, `global.event` is either undefined or retains whatever previous value it had, making code that expects this web-like behavior fail silently or behave incorrectly.

**Steps to reproduce:**
1. Register an event handler, e.g., on a `View` with `onTouchStart`.
2. Inside the handler, attempt to access `global.event`.
3. Observe that `global.event` is not the event object passed to the handler.

**Expected behavior:**
During the dispatch of a React Native event (Fabric legacy event system), the global `event` binding should be set to the event object before the listener is called, and restored to its previous value after the listener returns (including when nested events are dispatched). This matches the web specification and the behavior of the newer `EventTarget` implementation in React Native.

**Impact:**
- Breaks compatibility when porting web components and libraries that implicitly rely on `event` being globally available.
- Makes debugging and logging more cumbersome because you cannot simply inspect `global.event` when paused inside a handler.
- Prevents adopters from migrating gradually toward web-aligned event handling patterns.
- The missing global event conflicts with expectations set by React Native’s own internal “web-aligned” DOM event surface.

[RN] Expose event as a global variable during dispatch (#35913)


Ensure that React Native's Fabric attribute payload logic respects a view config’s `process` function for props whose values are functions, instead of unconditionally converting every function prop to `true`. Currently, `ReactNativeAttributePayloadFabric` always replaces function-typed props with `true` in `addNestedProperty` and `diffProperties`, which makes it impossible for a custom native component to receive the actual function. If the attribute configuration entry is an object with a `process` property, that function should be called with the raw prop value and its result used—even when the value is a function. When no `process` is defined, the existing behavior (converting the function to `true` for event handler marking) must be preserved. This applies both when creating initial props and when computing prop diffs.

feat(ReactNative): prioritize attribute config `process` function to allow processing function props (#32119)


**Title:** Suspense boundaries ignore state updates while timed‑out

**Area:** Suspense / reconciler (begin and unwind phases)

**Background**
When a `Suspense` boundary times out and switches to its fallback, any subsequent `setState` (or other priority updates) inside the primary tree is effectively swallowed – the fallback continues to show, and the new primary content is never attempted.

**Expected behavior**
An update inside a timed‑out `Suspense` subtree should trigger a fresh attempt to render the primary children, **without** waiting for the original promise to resolve.
- If the new render completes (does not suspend), the boundary should immediately commit the updated primary children in place of the fallback.
- If the new render suspends again, the boundary should remain in (or re‑enter) the fallback state, respecting the existing timeout mechanics.
- The re‑attempt should happen only when the primary child fragment has pending work with sufficient priority (i.e., `childExpirationTime` matches the current render).

**Key invariants to preserve**
1. **Primary fragment reuse:** When retrying the primary children, the work-in-progress primary fragment must be cloned from the *current* primary fragment (not the fallback fragment) to avoid losing state.
2. **Recapture decision:** If a boundary already timed out (`memoizedState.didTimeout === true`) and its primary children suspend again during a retry, the boundary must **not** re‑capture itself; the suspend should bubble to the next ancestor boundary. This logic should be centralized (e.g., a helper like `shouldCaptureSuspense`) to keep the unwind path consistent.
3. **Non‑concurrent mode:** Boundaries outside `ConcurrentMode` must still treat suspends as a synchronous “null‑render” (commit nothing, schedule a sync retry) without suspending the commit.

**Current gap**
In `beginWork`, the `SuspenseComponent` case bypasses `updateSuspenseComponent` entirely when `didTimeout` is true, skipping directly to the fallback sibling. This discards any pending work on the primary children.

**Ask**
Adjust the `SuspenseComponent` branch in `beginWork` so that when the boundary is timed out, it inspects the primary child fragment’s expiration time and, if work exists at the current render priority, calls `updateSuspenseComponent` to re‑evaluate the primary children. Ensure the unwind (`throwException`) capture logic correctly handles the case where an already‑timed‑out boundary’s primary children suspend again.

Try rendering again if a timed out tree receives an update (#13921)


## Request: Expose scheduler task priority via call-stack function names for profilers

### Background
When diagnosing performance issues in React applications, it is difficult to determine which scheduler priority is associated with a given piece of work simply by looking at a JavaScript profile or stack trace. Because the scheduler calls back into user code directly, the call stack loses the priority context, making it impossible for profiling tools (including React DevTools, browser DevTools, and custom profilers) to answer: “What priority is this work running at?”

### Desired Behavior
In development builds, whenever a scheduled task is executed (via `flushTask`), the scheduler should introduce an additional stack frame whose function name encodes the current priority level. This allows any tool that can read function names from a stack trace to derive the priority.

#### Acceptance Criteria
1. **Stack frame naming**
   - For each of the five priority levels (`Immediate`, `UserBlocking`, `Normal`, `Low`, `Idle`), a dedicated wrapper function with a name following the pattern `scheduler_flushTaskAtPriority_<PriorityLevel>` must appear on the call stack during the execution of a scheduled callback.
   - Example: if a user-blocking priority task runs, the stack should contain a frame for `scheduler_flushTaskAtPriority_UserBlocking`.

2. **Non-invasive**
   - The wrapper must simply call through to the original callback with the same arguments. It must not alter the return value, continuation callback handling, exception propagation, or any other observable behavior of the scheduler.
   - The implementation must not introduce unnecessary overhead in production builds; ideally it is guarded by a development-only condition (React’s `__DEV__` flag).

3. **Priority coverage**
   - All five scheduler priority levels must be represented. If a new priority is ever added, the same mechanism should be extended to it.

4. **Profiler consumption**
   - A developer (or tool) inspecting a captured call stack should be able to match a frame’s name against the known priority names and determine which priority the scheduler is currently flushing.

### Why This Matters
This change enables debugging workflows like:
- Detecting long tasks running at a lower-than-expected priority.
- Identifying priority inversion where high-priority work is blocked.
- Building automated profilers that annotate flame graphs with scheduler priority information.

By encoding the priority in the function name, tools do not need to instrument the scheduler internals — they simply read the stack. This is a lightweight, backwards-compatible enhancement that greatly improves observability.

[Scheduler] Support inferring priority from stack (#16105)


In React’s StrictMode, class component constructors, `static getDerivedStateFromProps`, `shouldComponentUpdate`, and the updater function passed to `setState` are intentionally called twice in development to help detect side‑effects. During the second call, console warnings/errors are suppressed via `disableLogs`. However, the returned value from that second call is currently thrown away and the function is then called a third time with logging re‑enabled – leading to duplicate warnings and extra work.

**Desired behaviour:**
Keep the double‑invocation, but use the result of the **second** (logs‑disabled) call as the real return value. Do not invoke the function a third time. The first call should still run with logging enabled so that the initial warnings are still emitted and can be deduplicated naturally on future renders.

**Acceptance criteria:**

- For `constructor`: in development StrictMode, the instance used is the one produced by the second `new` call (logs disabled).
- For `getDerivedStateFromProps`: the `partialState` used is the return value of the second invocation (logs disabled).
- For `shouldComponentUpdate`: the `shouldUpdate` decision is the boolean from the second invocation (logs disabled).
- For `setState` updaters: the updated state comes from the second invocation (logs disabled).
- In all cases, the **first** call’s result is ignored; only the second is kept.
- No third call occurs, and only the first call’s logs appear in the console (no duplicate warnings from the re‑enabled log pass).

This should apply to both developer and production builds, but production only calls once (no change there). The fix is scoped to React’s internal class component reconciliation logic.

Always keep disabled logs in the second pass (#21739)