nats-io/nats-server · error

can not unmarshal %q

Error message

can not unmarshal %q

What it means

RetentionPolicy.UnmarshalJSON accepts only the exact JSON strings for LimitsPolicy, InterestPolicy, and WorkQueuePolicy (case-sensitive, exact match on the raw bytes). Any other JSON input, including misspellings or different casing, hits the default branch and returns this error.

Source

Thrown at server/store.go:594

	case InterestPolicy:
		return interestPolicyJSONBytes, nil
	case WorkQueuePolicy:
		return workQueuePolicyJSONBytes, nil
	default:
		return nil, fmt.Errorf("can not marshal %v", rp)
	}
}

func (rp *RetentionPolicy) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case limitsPolicyJSONString:
		*rp = LimitsPolicy
	case interestPolicyJSONString:
		*rp = InterestPolicy
	case workQueuePolicyJSONString:
		*rp = WorkQueuePolicy
	default:
		return fmt.Errorf("can not unmarshal %q", data)
	}
	return nil
}

func (dp DiscardPolicy) String() string {
	switch dp {
	case DiscardOld:
		return "DiscardOld"
	case DiscardNew:
		return "DiscardNew"
	default:
		return "Unknown Discard Policy"
	}
}

func (dp DiscardPolicy) MarshalJSON() ([]byte, error) {
	switch dp {
	case DiscardOld:

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Correct the JSON value to one of the exact strings: "limits", "interest", or "workqueue" (lowercase, quoted).
  2. Pre-validate incoming JSON against the allowed values before unmarshaling.
  3. Regenerate the config file using a matching version of nats-streaming-server.

Example fix

// before
var rp stan.RetentionPolicy
json.Unmarshal([]byte(`"limits_policy"`), &rp) // error
// after
json.Unmarshal([]byte(`"limits"`), &rp) // ok
Defensive patterns

Strategy: validation

Validate before calling

var allowedRetention = map[string]bool{"\"limits\"": true, "\"interest\"": true, "\"workqueue\"": true}
if !allowedRetention[string(data)] {
	return fmt.Errorf("invalid retention policy %s", data)
}

Type guard

func parseRetentionPolicy(data []byte) (stan.RetentionPolicy, bool) {
	var rp stan.RetentionPolicy
	if err := rp.UnmarshalJSON(data); err != nil {
		return 0, false
	}
	return rp, true
}

Try / catch

if err := json.Unmarshal(data, &rp); err != nil {
	if strings.Contains(err.Error(), "can not unmarshal") {
		log.Printf("unknown retention policy %q, defaulting to limits", data)
		rp = stan.LimitsPolicy
		return nil
	}
	return err
}

Prevention

When it happens

Trigger: json.Unmarshal (or loading a channel config file / obtaining a Channel via the store) where the retention field contains a value other than "limits", "interest", or "workqueue" exactly.

Common situations: Hand-edited or generated stan.conf with e.g. "Limits" or "limit" instead of "limits"; config written by a different/older server version using other tokens; programmatically unmarshaling user-supplied JSON.

Related errors


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