nats-io/nats-server · error · JSConsumerInvalidPolicyError

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

Error message

consumer delivery policy is deliver %s, but optional start %s is also set

What it means

Consumer configuration validation detects a conflict between DeliverPolicy and an optional start position: the delivery policy already determines where to start (e.g. deliver all / last / new), but the user also supplied an explicit start (OptStartSeq or OptStartTime). The badStart helper formulates this error naming both the policy and the extra start option.

Source

Thrown at server/consumer.go:918

		}
	}
	subjectFilters := gatherSubjectFilters(config.FilterSubject, config.FilterSubjects)

	// 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"))
		}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Remove the redundant field: either clear OptStartSeq/OptStartTime or choose the corresponding DeliverPolicy (DeliverByStartSequence / DeliverByStartTime).
  2. Construct a fresh ConsumerConfig per request instead of mutating a shared struct.
  3. Validate config client-side (config.Validate() via the client library) before sending the consumer create/update API request.

Example fix

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

Strategy: validation

Validate before calling

func startConflict(cc *nats.ConsumerConfig) error {
    switch cc.DeliverPolicy {
    case nats.DeliverAll, nats.DeliverLast, nats.DeliverNew, nats.DeliverLastPerSubject:
        if cc.OptStartSeq > 0 || !cc.OptStartTime.IsZero() {
            return fmt.Errorf("deliver policy %v conflicts with explicit start", cc.DeliverPolicy)
        }
    case nats.DeliverByStartSequence:
        if !cc.OptStartTime.IsZero() {
            return fmt.Errorf("start-seq policy conflicts with OptStartTime")
        }
    case nats.DeliverByStartTime:
        if cc.OptStartSeq > 0 {
            return fmt.Errorf("start-time policy conflicts with OptStartSeq")
        }
    }
    return nil
}

Try / catch

_, err := js.AddConsumer(stream, cc)
if err != nil && strings.Contains(err.Error(), "is also set") {
    cc.OptStartSeq, cc.OptStartTime = 0, time.Time{} // strip conflicting starts
    return js.AddConsumer(stream, cc)
}

Prevention

When it happens

Trigger: ConsumerConfig with DeliverPolicy: DeliverAll/Last/New plus OptStartSeq > 0; DeliverPolicy: DeliverByStartSequence plus OptStartTime set; DeliverPolicy: DeliverByStartTime plus OptStartSeq set — checked in the start-position conflict switch at consumer.go:918.

Common situations: Reusing a partially filled ConsumerConfig struct across requests so a previous OptStartSeq/OptStartTime lingers; building config from JSON where both fields were present; library defaults populating one field while code sets another.

Related errors


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