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

  1. Use exactly one of the accepted JSON strings: the values mapped by ActionCreate/ActionUpdate/ActionCreateOrUpdate constants ("create", "update", "create_or_update").
  2. Drop the action field entirely if you want default behavior instead of guessing an alias.
  3. 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

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


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