nats-io/nats-server · error · JSStreamInvalidConfigError

preferred server not permitted in placement

Error message

preferred server not permitted in placement

What it means

JetStream rejects a consumer or stream config whose Placement block sets a Preferred server. The library explicitly forbids preferred-server placement for consumers for now, treating it as an invalid config. This is a policy restriction in the server, not a transient failure.

Source

Thrown at server/consumer.go:1027

				return NewJSConsumerEmptyGroupNameError()
			}
			if !validGroupName.MatchString(group) {
				return NewJSConsumerInvalidGroupNameError()
			}
		}
	} else {
		// If PriorityPolicy is None or not set, reject if PriorityGroups or PinnedTTL are set
		if len(config.PriorityGroups) > 0 {
			return NewJSConsumerPriorityGroupWithPolicyNoneError()
		}
		if config.PinnedTTL > 0 {
			return NewJSConsumerPinnedTTLWithoutPriorityPolicyNoneError()
		}
	}

	// For now don't allow preferred server in placement.
	if cfg.Placement != nil && cfg.Placement.Preferred != _EMPTY_ {
		return NewJSStreamInvalidConfigError(fmt.Errorf("preferred server not permitted in placement"))
	}

	return nil
}

func (mset *stream) addConsumerWithAction(config *ConsumerConfig, action ConsumerAction, pedantic bool) (*consumer, error) {
	return mset.addConsumerWithAssignment(config, _EMPTY_, nil, false, action, pedantic)
}

func (mset *stream) addConsumer(config *ConsumerConfig) (*consumer, error) {
	return mset.addConsumerWithAction(config, ActionCreateOrUpdate, false)
}

func (mset *stream) addConsumerWithAssignment(config *ConsumerConfig, oname string, ca *consumerAssignment, isRecovering bool, action ConsumerAction, pedantic bool) (*consumer, error) {
	return mset.addConsumerWithAssignmentAndMode(config, oname, ca, isRecovering, action, pedantic, false)
}

func (mset *stream) addConsumerForRestore(config *ConsumerConfig) (*consumer, error) {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Remove the Preferred field from the consumer's Placement config
  2. If server targeting is required, drop Placement entirely and rely on cluster-wide consumer or use streams (where Preferred is allowed)
  3. Set Placement.Preferred to "" (empty string) while keeping other placement fields like Tags/Cluster

Example fix

// before
 cfg.Placement = &Placement{Cluster: "east", Preferred: "nats-1"}
// after
 cfg.Placement = &Placement{Cluster: "east"}
Defensive patterns

Strategy: validation

Validate before calling

if cfg.Placement != nil && cfg.Placement.Preferred != "" {
    return fmt.Errorf("consumer placement must not set Preferred server")
}
// then call js.AddConsumer(stream, cfg)

Prevention

When it happens

Trigger: Calling AddConsumer (or creating/updating a consumer via js.AddConsumer or stream.AddConsumer with pedantic validation) with cfg.Placement != nil and cfg.Placement.Preferred set to a non-empty server name.

Common situations: Copying a stream's placement struct (which may legally carry Preferred) into a consumer config; migrating configs from tooling that assumed placement is fully shared between streams and consumers.

Related errors


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