You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PublishOptions.streamTimeout has no effect. The value is accepted, validated and stored, but never read by the library — so a publish uses the JetStream context's requestTimeout regardless of what the publish options say.
In 2.25.3, getStreamTimeout() has exactly one occurrence in the entire library: its own declaration at PublishOptions.java:104. Nothing calls it. NatsJetStream.publishSyncInternal (line 157) passes getTimeout() — the context's requestTimeout, per NatsJetStreamImpl's constructor — to makeInternalRequestResponseRequired, and never consults the publish options.
The setting appears functional because Builder.streamTimeout(Duration) validates the argument via validateDurationNotRequiredGtOrEqZero and stores it, and because the method is not deprecated (its neighbour Builder.stream(String) is). Its javadoc states:
Sets the timeout to wait for a publish acknowledgement from a JetStream enabled NATS server.
Measured, with PublishOptions.streamTimeout set and the context left at its default:
streamTimeout requested
publish actually failed after
PT4S
2.033 s
PT1S
2.036 s
(no publish options at all)
2.034 s
All three land on Options.DEFAULT_CONNECTION_TIMEOUT (2 s), which is what the context falls back to. The value has no effect in either direction, so it is not being clamped — it is unread.
Setting the two against each other confirms which one wins: a context with requestTimeout(Duration.ofSeconds(10)) and a publish with streamTimeout(Duration.ofSeconds(1)) fails after 10.03 s.
Expected behavior
Either of these would be fine — the current state is the problem, because the API documents a behaviour it does not implement:
streamTimeout bounds the wait for that publish's acknowledgement, as its javadoc says, taking precedence over the context's requestTimeout. This is the behaviour the Go client already provides — on Custom timeout for a JetStream API request not work nats.go#1246 a maintainer describes AckWait on js.Publish() as "a timeout waiting for the server to acknowledge the publish (if reached, ErrTimeout is returned)".
Or, if the context's requestTimeout is intended to be authoritative, then streamTimeout should be deprecated and its javadoc corrected to say it has no effect, so callers are not silently misled.
Today a caller that sets streamTimeout gets no timeout behaviour and no indication that the setting was ignored.
Server and client version
Client: jnats 2.25.3 (current release at time of writing). Also reproduced on 2.21.1 with identical timings.
Server: nats-server 2.12.3 (official nats:2.12.3 Docker image, started with -js).
Host environment
Linux 6.8.0 x86_64
OpenJDK (Temurin) 25.0.1+8
Server in Docker; client on the host.
Steps to reproduce
Run a JetStream-enabled server (nats-server -js, or docker run -p 4222:4222 nats:2.12.3 -js), then run this against jnats 2.25.3. The context is given a deadline of one nanosecond, which no network round trip can satisfy, while the publish itself is given a generous ten seconds — so if streamTimeout were honoured, the publish would comfortably succeed:
importio.nats.client.Connection;
importio.nats.client.JetStream;
importio.nats.client.JetStreamOptions;
importio.nats.client.Nats;
importio.nats.client.PublishOptions;
importio.nats.client.api.StreamConfiguration;
importjava.time.Duration;
publicclassStreamTimeoutRepro {
publicstaticvoidmain(String[] args) throwsException {
Connectionnc = Nats.connect("nats://localhost:4222");
nc.jetStreamManagement().addStream(StreamConfiguration.builder()
.name("repro")
.subjects("repro.subject")
.build());
// The context is given a deadline no round trip can meet...JetStreamjs = nc.jetStream(JetStreamOptions.builder()
.requestTimeout(Duration.ofNanos(1))
.build());
// ...while the publish itself is given a generous one.PublishOptionspublishOptions = PublishOptions.builder()
.streamTimeout(Duration.ofSeconds(10))
.build();
js.publish("repro.subject", "payload".getBytes(), publishOptions);
System.out.println("published - streamTimeout was honoured");
nc.close();
}
}
Expected: prints published, because the publish was allowed 10 seconds against a healthy local server.
Actual: throws immediately, with the context's 1 ns applied and the publish's 10 s discarded:
Exception in thread "main" java.io.IOException: Timeout or no response waiting for NATS JetStream server
at io.nats.client.impl.NatsJetStreamImpl.responseRequired(NatsJetStreamImpl.java:258)
at io.nats.client.impl.NatsJetStreamImpl.makeInternalRequestResponseRequired(NatsJetStreamImpl.java:249)
at io.nats.client.impl.NatsJetStream.publishSyncInternal(NatsJetStream.java:157)
at io.nats.client.impl.NatsJetStream.publish(NatsJetStream.java:70)
at StreamTimeoutRepro.main(StreamTimeoutRepro.java:29)
To see the reverse direction, invert the values — context requestTimeout(10s), publish streamTimeout(1s) — and stall the server after connecting but before publishing (kill -STOP the nats-server process, or docker pause the container, so the TCP connection stays open but no acknowledgement arrives). The publish fails after ~10 s rather than ~1 s.
Observed behavior
PublishOptions.streamTimeouthas no effect. The value is accepted, validated and stored, but never read by the library — so a publish uses the JetStream context'srequestTimeoutregardless of what the publish options say.In 2.25.3,
getStreamTimeout()has exactly one occurrence in the entire library: its own declaration atPublishOptions.java:104. Nothing calls it.NatsJetStream.publishSyncInternal(line 157) passesgetTimeout()— the context'srequestTimeout, perNatsJetStreamImpl's constructor — tomakeInternalRequestResponseRequired, and never consults the publish options.The setting appears functional because
Builder.streamTimeout(Duration)validates the argument viavalidateDurationNotRequiredGtOrEqZeroand stores it, and because the method is not deprecated (its neighbourBuilder.stream(String)is). Its javadoc states:Measured, with
PublishOptions.streamTimeoutset and the context left at its default:streamTimeoutrequestedPT4SPT1SAll three land on
Options.DEFAULT_CONNECTION_TIMEOUT(2 s), which is what the context falls back to. The value has no effect in either direction, so it is not being clamped — it is unread.Setting the two against each other confirms which one wins: a context with
requestTimeout(Duration.ofSeconds(10))and a publish withstreamTimeout(Duration.ofSeconds(1))fails after 10.03 s.Expected behavior
Either of these would be fine — the current state is the problem, because the API documents a behaviour it does not implement:
streamTimeoutbounds the wait for that publish's acknowledgement, as its javadoc says, taking precedence over the context'srequestTimeout. This is the behaviour the Go client already provides — on Custom timeout for a JetStream API request not work nats.go#1246 a maintainer describesAckWaitonjs.Publish()as "a timeout waiting for the server to acknowledge the publish (if reached,ErrTimeoutis returned)".requestTimeoutis intended to be authoritative, thenstreamTimeoutshould be deprecated and its javadoc corrected to say it has no effect, so callers are not silently misled.Today a caller that sets
streamTimeoutgets no timeout behaviour and no indication that the setting was ignored.Server and client version
nats:2.12.3Docker image, started with-js).Host environment
Steps to reproduce
Run a JetStream-enabled server (
nats-server -js, ordocker run -p 4222:4222 nats:2.12.3 -js), then run this against jnats 2.25.3. The context is given a deadline of one nanosecond, which no network round trip can satisfy, while the publish itself is given a generous ten seconds — so ifstreamTimeoutwere honoured, the publish would comfortably succeed:Expected: prints
published, because the publish was allowed 10 seconds against a healthy local server.Actual: throws immediately, with the context's 1 ns applied and the publish's 10 s discarded:
To see the reverse direction, invert the values — context
requestTimeout(10s), publishstreamTimeout(1s)— and stall the server after connecting but before publishing (kill -STOPthenats-serverprocess, ordocker pausethe container, so the TCP connection stays open but no acknowledgement arrives). The publish fails after ~10 s rather than ~1 s.