nats-io/nats-server · error · JSStreamRollupFailedError
10111
10111
Error message
rollup value invalid: %q
What it means
The 'Nats-Rollup' header value was not one of the two accepted values ('sub' to roll up all messages for the subject, 'all' to purge the entire stream). Any other string is rejected before the clustered proposal with JSStreamRollupFailedError (API error 10111). The error quotes the invalid value received.
Source
Thrown at server/jetstream_batching.go:946
if (!allowRollup || denyPurge) && !sourced {
err := errors.New("rollup not permitted")
return hdr, msg, 0, NewJSStreamRollupFailedError(err), err
}
switch rollup {
case JSMsgRollupSubject:
// Rolling up the subject is only allowed if the first occurrence of this subject in the batch.
if _, ok := diff.inflight[subject]; ok {
err := errors.New("batch rollup sub invalid")
return hdr, msg, 0, NewJSStreamRollupFailedError(err), err
}
case JSMsgRollupAll:
// Rolling up the whole stream is only allowed if this is the first message of the batch.
if len(diff.inflight) > 0 {
err := errors.New("batch rollup all invalid")
return hdr, msg, 0, NewJSStreamRollupFailedError(err), err
}
default:
err := fmt.Errorf("rollup value invalid: %q", rollup)
return hdr, msg, 0, NewJSStreamRollupFailedError(err), err
}
}
}
// Track inflight.
// Store the subject to ensure other messages in this batch using
// an expected check or rollup on the same subject fail.
if diff.inflight == nil {
diff.inflight = make(map[string]*inflightSubjectRunningTotal, 1)
}
var sz uint64
if mset.store.Type() == FileStorage {
sz = fileStoreMsgSizeRaw(len(subject), len(hdr), len(msg))
} else {
sz = memStoreMsgSizeRaw(len(subject), len(hdr), len(msg))
}
var (View on GitHub (pinned to 3a66a489d2)
Solutions
- Set Nats-Rollup to exactly "sub" or "all" (lowercase, no whitespace)
- Use the client library's constants (e.g. nats.MsgRollupSubject / nats.MsgRollupAll in Go) rather than raw strings
- Trim/validate any user-provided rollup option before placing it in the header
- Remember rollup requires AllowRollup on the stream; fix the value first, then verify stream config if the error changes to a permissions error
Example fix
// before
msg.Header.Set("Nats-Rollup", "Purge") // invalid
// after
msg.Header.Set("Nats-Rollup", nats.MsgRollupAll) // "all"; use nats.MsgRollupSubject for per-subject rollup Defensive patterns
Strategy: validation
Validate before calling
rollup := hdr.Get("Nats-Rollup")
if rollup != "" && rollup != "sub" && rollup != "all" {
return fmt.Errorf("invalid rollup value %q: must be \"sub\" or \"all\"", rollup)
}
Type guard
func validRollup(v string) bool { return v == "" || v == "sub" || v == "all" }
Try / catch
_, err := js.PublishMsg(msg)
var ae *nats.APIError
if errors.As(err, &ae) && ae.ErrorCode() == 10111 {
// reject/normalize the rollup header value before republishing
}
Prevention
- Use client constants (nats.MsgRollupSubject / nats.MsgRollupAll) instead of raw strings
- Trim and lower-case any user-supplied rollup option before setting the header
- Verify AllowRollup is enabled on the stream when rollups are intended
When it happens
Trigger: Setting Nats-Rollup to an unexpected string such as "Subject", "ALL", "true", "purge", or a value built via string interpolation; passing a bytes.Header value with trailing whitespace; a client library version mismatch where the rollup constant differs.
Common situations: Hand-written header values instead of using the client's MsgId/Rollup helper constants; configuration-driven rollups where a user-facing option string is forwarded verbatim into the header; case sensitivity mistakes (rollup values are lowercase).
Related errors
- JS_STREAM_ROLLUP_FAILED
- roll-ups require the purge permission
- 10111
- got corrupted escaped character
- unsupported BER encoding
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/55cb7c6d11cca170.
Report an issue: GitHub.