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

  1. Set MirrorDirect to true on the mirror config to match the origin's AllowDirect
  2. Remove the explicit MirrorDirect value so it inherits from the origin
  3. 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

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


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/eb5cbece453e5721. Report an issue: GitHub.