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
- Set the RetentionPolicy explicitly to LimitsPolicy, InterestPolicy, or WorkQueuePolicy before marshaling.
- Validate the policy value against the defined constants before calling json.Marshal.
- 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
- Always initialize policy fields with named constants, never with var/zero values.
- Validate channel configs before persisting or marshaling.
- Use the same nats-streaming-server version for writing and reading state files.
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.