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

  1. Pass a zero or positive Duration; use Duration.ZERO if you want no timeout
  2. Clamp computed durations: Duration.ofMillis(Math.max(0, computedMillis))
  3. 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

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.

Related errors


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)