nats-io/nats-server · error · JSStreamInvalidConfigError
duplicates window can not be negative
Error message
duplicates window can not be negative
What it means
JetStream stream config validation rejects a negative Duplicates window (time to cache recently published messages for duplicate detection). The library validates this in StreamConfig validation (server/stream.go) before creating or updating a stream, because a negative duration is meaningless. It is returned as a JSStreamInvalidConfigError so the client can correct the config.
Source
Thrown at server/stream.go:1975
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 {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("duplicates window needs to be >= 100ms"))
}
if cfg.DenyPurge && cfg.AllowRollup {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("roll-ups require the purge permission"))
}
// Counter is not compatible with some settings.View on GitHub (pinned to 3a66a489d2)
Solutions
- Set StreamConfig.Duplicates to a non-negative duration (e.g. 2*time.Minute, the server default).
- If unset is desired, leave Duplicates at 0 so the server applies its default instead of a negative value.
- Sanitize externally supplied config: clamp Duplicates with max(0, value) before calling the API.
- Check code that computes the window via subtraction for accidental negation.
Example fix
// before
cfg := jetstream.StreamConfig{Name: "ORDERS", Duplicates: -time.Second}
// after
cfg := jetstream.StreamConfig{Name: "ORDERS", Duplicates: 2 * time.Minute} Defensive patterns
Strategy: validation
Validate before calling
if cfg.Duplicates < 0 {
return fmt.Errorf("duplicates window must be >= 0, got %s", cfg.Duplicates)
} Try / catch
var scErr *jetstream.JSApiError
if _, err := js.CreateStream(ctx, cfg); errors.As(err, &scErr) && strings.Contains(scErr.Description, "duplicates window can not be negative") {
cfg.Duplicates = 0 // let server apply default
_, err = js.CreateStream(ctx, cfg)
} Prevention
- Never set negative durations in StreamConfig; use 0 for 'server default'.
- Clamp user/externally supplied Duplicates values with max(0, v).
- Audit duration math (subtraction) that feeds Duplicates for underflow.
When it happens
Trigger: Creating or updating a stream via JetStream API (AddStream/UpdateStream or jetstream.CreateStream) with StreamConfig.Duplicates set to a negative time.Duration value, e.g. -1 * time.Second.
Common situations: Config parsed from JSON/YAML where a negative number was entered; code computing the window from a clock offset or subtraction that underflows; unmarshalling user-supplied API payloads directly into StreamConfig without pre-validation.
Related errors
- duplicates window can not be larger then max age
- duplicates window needs to be >= 100ms
- max_ack_pending must be set to -1
- stream republish transform from '%s' to '%s': %w
- maximum replicas is %d
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/2517f08d9934b2bd.
Report an issue: GitHub.