nats-io/nats-server · error

can not marshal %v

Error message

can not marshal %v

What it means

RetentionPolicy.MarshalJSON in server/store.go only supports the defined enum values (LimitsPolicy, InterestPolicy, WorkQueuePolicy). If a RetentionPolicy variable holds an out-of-range value (e.g. zero value before assignment, or a value from an incompatible version), MarshalJSON hits the default branch and returns this error, which json.Marshal surfaces as an error wrapping "can not marshal %v".

Source

Thrown at server/store.go:581

	case InterestPolicy:
		return "Interest"
	case WorkQueuePolicy:
		return "WorkQueue"
	default:
		return "Unknown Retention Policy"
	}
}

func (rp RetentionPolicy) MarshalJSON() ([]byte, error) {
	switch rp {
	case LimitsPolicy:
		return limitsPolicyJSONBytes, nil
	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 {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Set the RetentionPolicy explicitly to LimitsPolicy, InterestPolicy, or WorkQueuePolicy before marshaling.
  2. Validate the policy value against the defined constants before calling json.Marshal.
  3. Check the source of the value (config file, network payload) for corruption or version mismatch and re-create the config with a current version of the library.

Example fix

// before
var rp stan.RetentionPolicy
b, err := json.Marshal(rp) // can not marshal 0
// after
rp := stan.LimitsPolicy
b, err := json.Marshal(rp) // ok
Defensive patterns

Strategy: validation

Validate before calling

func validRetentionPolicy(rp stan.RetentionPolicy) bool {
	return rp == stan.LimitsPolicy || rp == stan.InterestPolicy || rp == stan.WorkQueuePolicy
}

Type guard

func isRetentionPolicy(v int) (stan.RetentionPolicy, bool) {
	rp := stan.RetentionPolicy(v)
	switch rp {
	case stan.LimitsPolicy, stan.InterestPolicy, stan.WorkQueuePolicy:
		return rp, true
	}
	return 0, false
}

Try / catch

b, err := json.Marshal(cfg)
if err != nil {
	if strings.Contains(err.Error(), "can not marshal") {
		// fix or reject the policy value before retrying
	}
	return err
}

Prevention

When it happens

Trigger: Calling json.Marshal on a Channel or ChannelConfig whose RetentionPolicy field was never set to one of the three defined constants (e.g. declared as var rp RetentionPolicy leaving 0 invalid, or deserialized from a file with an unknown value).

Common situations: Persisting NATS Streaming channel configuration to disk with a corrupted/zeroed field; upgrading or downgrading nats-streaming-server between versions where the policy enum differs; hand-editing a config file with an invalid policy string that was silently mapped to an invalid numeric value.

Related errors


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