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

  1. Use the exact accepted string ("async" for async persist; "default" or "" otherwise).
  2. Remove the persist_mode field entirely to get the default mode.
  3. 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

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


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