nats-io/nats-server · error · ApiError (JSStreamInvalidConfigF)
10052
10052
Error message
counter stream cannot use message TTLs
What it means
Counter streams (AllowMsgCounter=true) cannot also enable per-message TTLs (AllowMsgTTL): message-level expiration interferes with counter state consistency, so server/stream.go validation rejects the combination. Returned as a JSStreamInvalidConfigError. The associated code for these newer counter validation errors is 10052 (JSStreamInvalidConfig).
Source
Thrown at server/stream.go:1999
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.
if cfg.AllowMsgCounter {
if cfg.Discard == DiscardNew {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("counter stream cannot use discard new"))
}
if cfg.AllowMsgTTL {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("counter stream cannot use message TTLs"))
}
if cfg.AllowMsgSchedules {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("counter stream cannot use message schedules"))
}
if cfg.Retention != LimitsPolicy {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("counter stream can only use limits retention"))
}
}
// Check for new discard new per subject, we require the discard policy to also be new.
if cfg.DiscardNewPer {
if cfg.Discard != DiscardNew {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("discard new per subject requires discard new policy to be set"))
}
if cfg.MaxMsgsPer <= 0 {
return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("discard new per subject requires max msgs per subject > 0"))
}
}View on GitHub (pinned to 3a66a489d2)
Solutions
- Set AllowMsgTTL=false on the counter stream.
- Split workloads: use a separate stream for TTL'd messages, keeping the counter stream TTL-free.
- Or drop AllowMsgCounter and implement counters at the application level if TTLs are essential.
- Upgrade/align client and server versions and re-check docs, since these are recent JetStream features.
Example fix
// before
StreamConfig{Name: "CTR", AllowMsgCounter: true, AllowMsgTTL: true}
// after
StreamConfig{Name: "CTR", AllowMsgCounter: true, AllowMsgTTL: false} Defensive patterns
Strategy: validation
Validate before calling
if cfg.AllowMsgCounter && cfg.AllowMsgTTL {
return fmt.Errorf("counter streams cannot enable message TTLs")
} Try / catch
var scErr *jetstream.JSApiError
if _, err := js.CreateStream(ctx, cfg); errors.As(err, &scErr) && strings.Contains(scErr.Description, "counter stream cannot use message TTLs") {
cfg.AllowMsgTTL = false
_, err = js.CreateStream(ctx, cfg)
} Prevention
- Audit publishers for Nats-Msg-Ttl headers before enabling AllowMsgCounter.
- Split TTL workloads onto a separate stream.
- Check the NATS server feature compatibility notes for counters.
When it happens
Trigger: AddStream/UpdateStream with AllowMsgCounter=true and AllowMsgTTL=true, e.g. a stream that both increments counters and sets Nats-Msg-Ttl headers.
Common situations: Enabling all modern stream features (counters, msg TTLs, schedules) on one stream from a feature checklist; migrating a stream to counters without auditing existing publishers that send TTL headers.
Related errors
- counter stream cannot use discard new
- duplicates window can not be negative
- duplicates window can not be larger then max age
- duplicates window can not be larger then server limit of %v
- duplicates window needs to be >= 100ms
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/b60d4b6952982f1e.
Report an issue: GitHub.