nats-io/nats-server · error · ApiError

consumer durable name can not contain '.', '*', '>', '\', '/

Error message

consumer durable name can not contain '.', '*', '>', '\', '/'

What it means

The consumer's Durable field failed isValidAssetName because it contains '.', '*', '>', '\\' or '/'. Durable names become part of internal subjects and storage keys, so wildcard/path characters are forbidden. The server rejects the request with a JSStreamInvalidConfigError.

Source

Thrown at server/consumer.go:742

	}
	return nil
}

// Check the consumer config. If we are recovering don't check filter subjects.
func checkConsumerCfg(
	config *ConsumerConfig,
	srvLim *JSLimitOpts,
	cfg *StreamConfig,
	_ *Account,
	accLim *JetStreamAccountLimits,
	isRecovering bool,
) *ApiError {

	if config.Name != _EMPTY_ && !isValidAssetName(config.Name) {
		return NewJSStreamInvalidConfigError(errors.New("consumer name can not contain '.', '*', '>', '\\', '/'"))
	}
	if config.Durable != _EMPTY_ && !isValidAssetName(config.Durable) {
		return NewJSStreamInvalidConfigError(errors.New("consumer durable name can not contain '.', '*', '>', '\\', '/'"))
	}

	// Check if replicas is defined but exceeds parent stream.
	if config.Replicas > 0 && config.Replicas > cfg.Replicas {
		return NewJSConsumerReplicasExceedsStreamError()
	}
	// Check that it is not negative
	if config.Replicas < 0 {
		return NewJSReplicasCountCannotBeNegativeError()
	}
	// If the stream is interest or workqueue retention make sure the replicas
	// match that of the stream. This is REQUIRED for now.
	if cfg.Retention == InterestPolicy || cfg.Retention == WorkQueuePolicy {
		// Only error here if not recovering.
		// We handle recovering in a different spot to allow consumer to come up
		// if previous version allowed it to be created. We do not want it to not come up.
		if !isRecovering && config.Replicas != 0 && config.Replicas != cfg.Replicas {
			return NewJSConsumerReplicasShouldMatchStreamError()

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Replace forbidden characters in the durable name with '-', '_' or alphanumerics.
  2. Add a sanitize/validate step before constructing ConsumerConfig.
  3. Let the server generate the durable name by omitting Durable when ephemeral behavior is acceptable.
  4. Keep durable names in one canonical format across services to avoid recurring validation failures.

Example fix

// before
cc := &nats.ConsumerConfig{Durable: "billing.worker.v2"}
// after
cc := &nats.ConsumerConfig{Durable: "billing-worker-v2"}
Defensive patterns

Strategy: validation

Validate before calling

var invalidAssetNameRe = regexp.MustCompile(`[.*>\\/]`)
func validDurableName(durable string) bool {
	return durable == "" || !invalidAssetNameRe.MatchString(durable)
}
if !validDurableName(cc.Durable) {
	return fmt.Errorf("invalid durable name %q", cc.Durable)
}

Prevention

When it happens

Trigger: Any consumer create/update call where config.Durable is non-empty and contains one of the forbidden characters: '.', '*', '>', '\\' or '/'.

Common situations: Using domain-like durable names such as 'my.app.worker'; reusing subscription subject strings as durable names; copy-pasting names from configs with slashes; clients from other ecosystems (e.g. Kafka group ids with dots) carried over.

Related errors


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