nats-io/nats-server · error
unknown consumer action: %v
Error message
unknown consumer action: %v
What it means
ConsumerConfig.Action is deserialized from JSON via UnmarshalJSON; the value must be one of the known JSON strings (create / update / create_or_update). Any other string fails unmarshalling with this error, so an invalid Action in a consumer create/update request JSON is rejected at parse time.
Source
Thrown at server/consumer.go:231
case ActionUpdate:
return actionUpdateJSONBytes, nil
case ActionCreateOrUpdate:
return actionCreateOrUpdateJSONBytes, nil
default:
return nil, fmt.Errorf("can not marshal %v", a)
}
}
func (a *ConsumerAction) UnmarshalJSON(data []byte) error {
switch string(data) {
case actionCreateJSONString:
*a = ActionCreate
case actionUpdateJSONString:
*a = ActionUpdate
case actionCreateOrUpdateJSONString:
*a = ActionCreateOrUpdate
default:
return fmt.Errorf("unknown consumer action: %v", string(data))
}
return nil
}
// ConsumerNakOptions is for optional NAK values, e.g. delay.
type ConsumerNakOptions struct {
Delay time.Duration `json:"delay"`
}
// PriorityPolicy determines policy for selecting messages based on priority.
type PriorityPolicy int
const (
// No priority policy.
PriorityNone PriorityPolicy = iota
// Clients will get the messages only if certain criteria are specified.
PriorityOverflow
// Single client takes over handling of the messages, while others are on standby.View on GitHub (pinned to 3a66a489d2)
Solutions
- Use exactly one of the accepted JSON strings: the values mapped by ActionCreate/ActionUpdate/ActionCreateOrUpdate constants ("create", "update", "create_or_update").
- Drop the action field entirely if you want default behavior instead of guessing an alias.
- Check the NATS server version's consumer.go constants to confirm which action strings your server supports.
Example fix
// before
{"durable_name": "d1", "action": "upsert"}
// after
{"durable_name": "d1", "action": "create_or_update"} Defensive patterns
Strategy: validation
Validate before calling
validActions := map[string]bool{"create": true, "update": true, "create_or_update": true}
if cfg.Action != "" && !validActions[cfg.Action] {
return fmt.Errorf("invalid action %q", cfg.Action)
} Type guard
func isValidConsumerAction(s string) bool {
switch s {
case "create", "update", "create_or_update":
return true
}
return false
} Try / catch
_, err := js.AddConsumer(stream, cfg)
if err != nil && strings.Contains(err.Error(), "unknown consumer action") {
cfg.Action = "" // or "create_or_update"
return js.AddConsumer(stream, cfg)
} Prevention
- Only send "create", "update", or "create_or_update" — never "upsert"
- Use the typed Action constants from the client library instead of raw strings
- Check your server version's accepted action strings when mixing versions
When it happens
Trigger: Posting consumer config JSON with "action": "upsert" (a commonly assumed alias that does not exist), a typo like "creat", wrong casing, or an Action field serialized as a number/bool instead of a recognized string.
Common situations: Migrating from other systems where "upsert" was the term; SDK/client version drift where new action values are sent to an older server; hand-written curl payloads for the $JS.API.CONSUMER.CREATE subjects.
Related errors
- JS_INVALID_JSON
- unknown priority policy: %v
- got corrupted escaped character
- incomplete type, value pair
- DN ended with incomplete type, value pair
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/316831085979c5fa.
Report an issue: GitHub.