nats-io/nats-server · error · JSStreamInvalidConfigError

invalid discard policy

Error message

invalid discard policy

What it means

checkStreamCfgLocked: the stream's Discard policy failed to marshal/unmarshal as a valid DiscardPolicy enum — the configured discard value is not one of the recognized policies (old/new or limits/new). Surfaced as NewJSStreamInvalidConfigError; the API layer maps it to an invalid stream configuration error.

Source

Thrown at server/stream.go:1897

	if len(config.Description) > JSMaxDescriptionLen {
		return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("stream description is too long, maximum allowed is %d", JSMaxDescriptionLen))
	}

	var metadataLen int
	for k, v := range config.Metadata {
		metadataLen += len(k) + len(v)
	}
	if metadataLen > JSMaxMetadataLen {
		return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("stream metadata exceeds maximum size of %d bytes", JSMaxMetadataLen))
	}

	cfg := *config

	if _, err := cfg.Retention.MarshalJSON(); err != nil {
		return cfg, NewJSStreamInvalidConfigError(fmt.Errorf("invalid retention"))
	}
	if _, err := cfg.Discard.MarshalJSON(); err != nil {
		return cfg, NewJSStreamInvalidConfigError(fmt.Errorf("invalid discard policy"))
	}
	if _, err := cfg.Compression.MarshalJSON(); err != nil {
		return cfg, NewJSStreamInvalidConfigError(fmt.Errorf("invalid compression"))
	}

	// Make file the default.
	if cfg.Storage == 0 {
		cfg.Storage = FileStorage
	}
	if _, err := cfg.Storage.MarshalJSON(); err != nil {
		return cfg, NewJSStreamInvalidConfigError(fmt.Errorf("invalid storage type"))
	}

	if cfg.Replicas == 0 {
		cfg.Replicas = 1
	}
	if cfg.Replicas > StreamMaxReplicas {
		return cfg, NewJSStreamInvalidConfigError(fmt.Errorf("maximum replicas is %d", StreamMaxReplicas))

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Set Discard to jetstream.DiscardOld or jetstream.DiscardNew explicitly.
  2. Omit the field to accept the default (DiscardOld).
  3. Validate the enum before submitting; log its numeric value if unsure.
  4. When using raw JSON requests, use the correct numeric enum value.

Example fix

// before
cfg := jetstream.StreamConfig{Name: "ORDERS", Discard: jetstream.DiscardPolicy(5)}
// after
cfg := jetstream.StreamConfig{Name: "ORDERS", Discard: jetstream.DiscardNew}
Defensive patterns

Strategy: validation

Validate before calling

func validDiscard(d jetstream.DiscardPolicy) error {
  switch d {
  case jetstream.DiscardOld, jetstream.DiscardNew:
    return nil
  }
  return fmt.Errorf("invalid discard policy: %d", int(d))
}

Type guard

func isKnownDiscard(d jetstream.DiscardPolicy) bool {
  return d == jetstream.DiscardOld || d == jetstream.DiscardNew
}

Prevention

When it happens

Trigger: Passing an invalid cast value like DiscardPolicy(5), an uninitialized enum from an untyped struct, or decoding a config where 'discard' is a string ("old") instead of the expected enum in raw API JSON.

Common situations: Config templating that interpolates wrong types into the discard field; cross-version enum mismatches; copy-pasted constants from other messaging systems.

Related errors


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