nats-io/nats-server · error · JSConsumerInvalidPolicyError

consumer delivery policy is deliver %s, but optional %s is n

Error message

consumer delivery policy is deliver %s, but optional %s is not set

What it means

The inverse of the previous conflict: the chosen DeliverPolicy requires an optional start value that was not provided. The notSet helper returns this error naming the policy and the missing option — e.g. DeliverByStartSequence with OptStartSeq == 0, or DeliverByStartTime with a zero OptStartTime.

Source

Thrown at server/consumer.go:921

	// Check subject filters do not overlap.
	for outer, subject := range subjectFilters {
		if !IsValidSubject(subject) {
			return NewJSStreamInvalidConfigError(ErrBadSubject)
		}
		for inner, ssubject := range subjectFilters {
			if inner != outer && subjectIsSubsetMatch(subject, ssubject) {
				return NewJSConsumerOverlappingSubjectFiltersError()
			}
		}
	}

	// Helper function to formulate similar errors.
	badStart := func(dp, start string) error {
		return fmt.Errorf("consumer delivery policy is deliver %s, but optional start %s is also set", dp, start)
	}
	notSet := func(dp, notSet string) error {
		return fmt.Errorf("consumer delivery policy is deliver %s, but optional %s is not set", dp, notSet)
	}

	// Check on start position conflicts.
	switch config.DeliverPolicy {
	case DeliverAll:
		if config.OptStartSeq > 0 {
			return NewJSConsumerInvalidPolicyError(badStart("all", "sequence"))
		}
		if config.OptStartTime != nil {
			return NewJSConsumerInvalidPolicyError(badStart("all", "time"))
		}
	case DeliverLast:
		if config.OptStartSeq > 0 {
			return NewJSConsumerInvalidPolicyError(badStart("last", "sequence"))
		}
		if config.OptStartTime != nil {
			return NewJSConsumerInvalidPolicyError(badStart("last", "time"))
		}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Supply the matching value: set OptStartSeq to the desired sequence when using DeliverByStartSequence, or OptStartTime when using DeliverByStartTime.
  2. If you actually want everything, switch DeliverPolicy to DeliverAll instead of by-sequence/time.
  3. Validate the consumer config client-side before submitting to catch the missing pairing early.

Example fix

// before
cc := nats.ConsumerConfig{DeliverPolicy: nats.DeliverByStartSequence}
// after
cc := nats.ConsumerConfig{DeliverPolicy: nats.DeliverByStartSequence, OptStartSeq: 1000}
Defensive patterns

Strategy: validation

Validate before calling

func requiresStart(cc *nats.ConsumerConfig) error {
    switch cc.DeliverPolicy {
    case nats.DeliverByStartSequence:
        if cc.OptStartSeq == 0 {
            return fmt.Errorf("DeliverByStartSequence requires OptStartSeq")
        }
    case nats.DeliverByStartTime:
        if cc.OptStartTime.IsZero() {
            return fmt.Errorf("DeliverByStartTime requires OptStartTime")
        }
    }
    return nil
}

Try / catch

_, err := js.AddConsumer(stream, cc)
if err != nil && strings.Contains(err.Error(), "is not set") {
    cc.DeliverPolicy = nats.DeliverAll // safe fallback
    return js.AddConsumer(stream, cc)
}

Prevention

When it happens

Trigger: ConsumerConfig with DeliverPolicy: DeliverByStartSequence but OptStartSeq left at 0; DeliverPolicy: DeliverByStartTime but OptStartTime unset (zero time).

Common situations: Setting only the deliver policy enum from CLI flags/UI and forgetting the matching start value; JSON payloads missing the optional field; defaults not filled in by an older client library.

Related errors


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