OpenFeign/feign · error · IllegalArgumentException
eventTimeout must not be negative
Error message
eventTimeout must not be negative: ${eventTimeout} What it means
GraphqlDecoder's constructor validates the subscription event timeout: a negative Duration is nonsensical (no events could ever be delivered in negative time), so it fails fast with IllegalArgumentException.
Solutions
- Pass a zero or positive Duration; use Duration.ZERO if you want no timeout
- Clamp computed durations: Duration.ofMillis(Math.max(0, computedMillis))
- Interpret 'disabled' config values explicitly rather than encoding them as negative durations
Example fix
// before GraphqlDecoder d = new GraphqlDecoder(json, Duration.parse(cfg.timeout), exec); // -1s // after Duration t = Duration.parse(cfg.timeout); GraphqlDecoder d = new GraphqlDecoder(json, t.isNegative() ? Duration.ZERO : t, exec);
Defensive patterns
Strategy: validation
Validate before calling
Duration t = Duration.parse(cfg.timeout()); if (t.isNegative()) t = Duration.ZERO; // or fail fast with a clear message
Try / catch
try {
return new GraphqlDecoder(json, timeout, executor);
} catch (IllegalArgumentException e) {
throw new ConfigException("eventTimeout must be >= 0, got: " + timeout);
} Prevention
- Clamp or validate durations loaded from config before constructing decoders
- Never use negative durations to encode 'disabled'; use Duration.ZERO or Optional
- Add unit tests for edge-case config values (0, -1)
When it happens
Trigger: new GraphqlDecoder(jsonDecoder, Duration.ofSeconds(-1), executor) or any negative Duration passed as eventTimeout; typically from misread config (e.g. '-1' meaning 'disable') or a computed duration that went negative (end before start).
Common situations: Configuration property like graphql.eventTimeout=-1 intended as 'infinite', or subtracting timestamps to compute a timeout without clamping.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Status Code [ ] has already been declared to throw [ ] and…
- target values must be absolute.
- Target is not a valid URI.
- Expected a Class, ParameterizedType, or GenericArrayType…
- formatted errorMessageTemplate with errorMessageArgs
AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10).
Data as JSON: /api/errors/b5b67bcb130cd1b6.
Report an issue: GitHub.
Appendix: source
Thrown at graphql/src/main/java/feign/graphql/GraphqlDecoder.java:58
import java.util.stream.Stream;
@Experimental
public class GraphqlDecoder implements Decoder {
/** How long a blocking subscription call waits for an event before giving up. */
public static final Duration DEFAULT_EVENT_TIMEOUT = Duration.ofSeconds(60);
private final JsonDecoder jsonDecoder;
private final long eventTimeoutMillis;
private final Executor executor;
public GraphqlDecoder(JsonDecoder jsonDecoder) {
this(jsonDecoder, DEFAULT_EVENT_TIMEOUT, Runnable::run);
}
public GraphqlDecoder(JsonDecoder jsonDecoder, Duration eventTimeout, Executor executor) {
if (eventTimeout.isNegative()) {
throw new IllegalArgumentException("eventTimeout must not be negative: " + eventTimeout);
}
this.jsonDecoder = jsonDecoder;
this.eventTimeoutMillis = eventTimeout.toMillis();
this.executor = executor;
}
@Override
public Object decode(Response response, Type type) throws IOException {
if (response.body() instanceof Subscription subscription) {
return subscribe(subscription, type);
}
Type targetType = type;
boolean optional = isOptionalType(type);
if (optional) {
targetType = extractOptionalInnerType(type);
}
View on GitHub (pinned to e2a1e27560)