nats-io/nats-server · error · JSPedanticError
duplicate window cannot be bigger than max age
Error message
duplicate window cannot be bigger than max age
What it means
In pedantic mode, if no explicit Duplicates window is set and the effective duplicate window (default or account-limited) is larger than the stream's MaxAge, the server rejects the config: a duplicate window longer than max age is meaningless since messages expire before dedup tracking ends. Non-pedantic mode silently sets Duplicates = MaxAge.
Source
Thrown at server/stream.go:1967
}
if cfg.MaxAge < 0 {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("max age can not be negative"))
}
if cfg.MaxAge != 0 && cfg.MaxAge < 100*time.Millisecond {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("max age needs to be >= 100ms"))
}
if cfg.Duplicates == 0 && cfg.Mirror == nil && len(cfg.Sources) == 0 {
maxWindow := StreamDefaultDuplicatesWindow
if lim.Duplicates > 0 && maxWindow > lim.Duplicates {
if pedantic {
return StreamConfig{}, NewJSPedanticError(fmt.Errorf("duplicate window limits are higher than current limits"))
}
maxWindow = lim.Duplicates
}
if cfg.MaxAge != 0 && cfg.MaxAge < maxWindow {
if pedantic {
return StreamConfig{}, NewJSPedanticError(fmt.Errorf("duplicate window cannot be bigger than max age"))
}
cfg.Duplicates = cfg.MaxAge
} else {
cfg.Duplicates = maxWindow
}
}
if cfg.Duplicates < 0 {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("duplicates window can not be negative"))
}
// Check that duplicates is not larger then age if set.
if cfg.MaxAge != 0 && cfg.Duplicates > cfg.MaxAge {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("duplicates window can not be larger then max age"))
}
if lim.Duplicates > 0 && cfg.Duplicates > lim.Duplicates {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("duplicates window can not be larger then server limit of %v",
lim.Duplicates.String()))
}
if cfg.Duplicates > 0 && cfg.Duplicates < 100*time.Millisecond {View on GitHub (pinned to 3a66a489d2)
Solutions
- Set cfg.Duplicates <= cfg.MaxAge explicitly
- Increase cfg.MaxAge to at least the desired duplicate window
- Omit pedantic mode so the server silently sets Duplicates=MaxAge
Example fix
// before
StreamConfig{Name: "ORDERS", MaxAge: time.Minute} // default window 2m > max age
// after
StreamConfig{Name: "ORDERS", MaxAge: time.Minute, Duplicates: time.Minute} Defensive patterns
Strategy: validation
Validate before calling
if cfg.MaxAge != 0 && cfg.Duplicates == 0 {
cfg.Duplicates = cfg.MaxAge // keep window <= max age
} else if cfg.Duplicates > cfg.MaxAge && cfg.MaxAge != 0 {
cfg.Duplicates = cfg.MaxAge
} Try / catch
if err != nil {
var apiErr *nats.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode == nats.ErrorCodeJetStreamInvalidStreamConfig {
cfg.Duplicates = cfg.MaxAge
err = createStream(cfg)
}
} Prevention
- Always keep Duplicates <= MaxAge when MaxAge is set
- Set both fields together in a single config helper
- Enable pedantic checks in CI so window/age mismatches surface before production
When it happens
Trigger: Pedantic stream create/update where cfg.Duplicates==0 (no mirror/sources), MaxAge is set and greater than 0 but smaller than maxWindow (StreamDefaultDuplicatesWindow, possibly capped by account limits). server/stream.go:1967.
Common situations: Streams configured with short retention (e.g. max_age=1m) that inherit the default 2-minute duplicate window; users unaware dedup window and max age interact; enabling pedantic validation exposing previously silent coercion.
Related errors
- max_msgs must be set to -1
- max_msgs_per_subject must be set to -1
- max_bytes must be set to -1
- max_msg_size must be set to -1
- max_consumers must be set to -1
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/fc09551842cb603a.
Report an issue: GitHub.