nats-io/nats-server · error · ApiError (JSPedanticErrF)
10157
10157
Error message
origin stream has direct get set, mirror has it disabled
What it means
In pedantic mode, when a mirror's origin stream is found locally, the server compares cfg.MirrorDirect with the origin's AllowDirect setting. If the mirror's MirrorDirect flag disagrees (typically mirror has it disabled while origin has direct gets), JSPedanticError (10157) is returned instead of silently inheriting the value.
Source
Thrown at server/stream.go:2171
// checking against itself (if sourced stream name is the same on different JetStream)
if cfg.Mirror.External == nil {
if !isValidAssetName(cfg.Mirror.Name) {
return StreamConfig{}, NewJSMirrorInvalidStreamNameError()
}
// We do not require other stream to exist anymore, but if we can see it check payloads.
exists, maxMsgSize, subs := hasStream(cfg.Mirror.Name)
if len(subs) > 0 {
streamSubs = append(streamSubs, subs...)
}
if exists {
if cfg.MaxMsgSize > 0 && maxMsgSize > 0 && cfg.MaxMsgSize < maxMsgSize {
return StreamConfig{}, NewJSMirrorMaxMessageSizeTooBigError()
}
}
// Determine if we are inheriting direct gets.
if exists, ocfg := getStream(cfg.Mirror.Name); exists {
if pedantic && cfg.MirrorDirect != ocfg.AllowDirect {
return StreamConfig{}, NewJSPedanticError(fmt.Errorf("origin stream has direct get set, mirror has it disabled"))
}
cfg.MirrorDirect = ocfg.AllowDirect
} else if js := s.getJetStream(); js != nil && js.isClustered() {
// Could not find it here. If we are clustered we can look it up.
if sa := js.streamAssignmentOrInflight(acc.Name, cfg.Mirror.Name); sa != nil {
if pedantic && cfg.MirrorDirect != sa.Config.AllowDirect {
return StreamConfig{}, NewJSPedanticError(fmt.Errorf("origin stream has direct get set, mirror has it disabled"))
}
cfg.MirrorDirect = sa.Config.AllowDirect
}
}
} else {
if cfg.Mirror.External.DeliverPrefix != _EMPTY_ {
deliveryPrefixes = append(deliveryPrefixes, cfg.Mirror.External.DeliverPrefix)
}
if cfg.Mirror.External.ApiPrefix != _EMPTY_ {
apiPrefixes = append(apiPrefixes, cfg.Mirror.External.ApiPrefix)View on GitHub (pinned to 3a66a489d2)
Solutions
- Set MirrorDirect to true on the mirror config to match the origin's AllowDirect
- Remove the explicit MirrorDirect value so it inherits from the origin
- Set AllowDirect: false on the origin stream if direct gets are not wanted
Example fix
// before
sc := StreamConfig{Name: "m", Mirror: &StreamSource{Name: "origin"}, MirrorDirect: false}
// after
sc := StreamConfig{Name: "m", Mirror: &StreamSource{Name: "origin"}, MirrorDirect: true} Defensive patterns
Strategy: validation
Validate before calling
if origin != nil && cfg.MirrorDirect != origin.AllowDirect {
cfg.MirrorDirect = origin.AllowDirect // inherit before AddStream
} Try / catch
var pe *jsapi.APIError
if errors.As(err, &pe) && pe.ErrorCode == 10157 {
// fetch origin AllowDirect, set MirrorDirect accordingly, retry
} Prevention
- Don't hard-code MirrorDirect; inherit it from the origin stream
- Check origin's AllowDirect whenever templates change
- Enable pedantic checks in CI to catch drift early
When it happens
Trigger: AddStream/UpdateStream of a Mirror with pedantic checks enabled, where getStream(cfg.Mirror.Name) succeeds locally and cfg.MirrorDirect != origin cfg.AllowDirect.
Common situations: Creating a mirror with MirrorDirect explicitly false/omitted while the origin stream has AllowDirect: true; templates that hard-code MirrorDirect=false; migrating configs between environments where the origin was changed to AllowDirect.
Related errors
- subject filter '%s' for the mirror %w
- invalid subject transform source '%s' for the mirror: %w
- subject transform from '%s' to '%s' for the mirror: %w
- max_msgs must be set to -1
- max_msgs_per_subject must be set to -1
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/eb5cbece453e5721.
Report an issue: GitHub.