nats-io/nats-server · error
can not unmarshal %q
Error message
can not unmarshal %q
What it means
PersistModeType.UnmarshalJSON rejects JSON values that are neither the recognized persist-mode strings (e.g. "default", "async") nor an empty string. The stream config decode fails when persist_mode contains an unknown value.
Source
Thrown at server/stream.go:238
func (wc PersistModeType) MarshalJSON() ([]byte, error) {
switch wc {
case DefaultPersistMode:
return defaultPersistModeJSONBytes, nil
case AsyncPersistMode:
return asyncPersistModeJSONBytes, nil
default:
return nil, fmt.Errorf("can not marshal %v", wc)
}
}
func (wc *PersistModeType) UnmarshalJSON(data []byte) error {
switch string(data) {
case defaultPersistModeJSONString, `""`:
*wc = DefaultPersistMode
case asyncPersistModeJSONString:
*wc = AsyncPersistMode
default:
return fmt.Errorf("can not unmarshal %q", data)
}
return nil
}
// JSPubAckResponse is a formal response to a publish operation.
type JSPubAckResponse struct {
Error *ApiError `json:"error,omitempty"`
*PubAck
}
// ToError checks if the response has a error and if it does converts it to an error
// avoiding the pitfalls described by https://yourbasic.org/golang/gotcha-why-nil-error-not-equal-nil/
func (r *JSPubAckResponse) ToError() error {
if r.Error == nil {
return nil
}
return r.Error
}View on GitHub (pinned to 3a66a489d2)
Solutions
- Use the exact accepted string ("async" for async persist; "default" or "" otherwise).
- Remove the persist_mode field entirely to get the default mode.
- Use a typed client constant rather than a hand-written string.
Example fix
// before
{"persist_mode": "asyncronous"}
// after
{"persist_mode": "async"} Defensive patterns
Strategy: validation
Validate before calling
pm := cfg.PersistMode
if pm != "async" && pm != "default" && pm != "" { return fmt.Errorf("invalid persist_mode %q", pm) } Type guard
func isPersistModeString(s string) bool {
return s == "async" || s == "default" || s == ""
} Prevention
- Prefer the typed PersistMode constants over raw strings.
- Omit persist_mode to get the default.
When it happens
Trigger: Decoding a StreamConfig whose persist_mode field is a typo, wrong case, or an arbitrary string not in the accepted set.
Common situations: Configs edited by hand, values copied from docs of a different version, or JSON produced by another system with different persist-mode naming.
Related errors
- can not marshal %v
- JS_INVALID_JSON
- unknown consumer action: %v
- unknown priority policy: %v
- %w at offset %d
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/b323f5ecfc8f0da5.
Report an issue: GitHub.