t8y2/dbx · error

RocketMQ topic retention is broker-level and cannot be chang

Error message

RocketMQ topic retention is broker-level and cannot be changed per topic

What it means

alterTopicConfig rejects attempts to set retention.ms or retention.bytes on an individual RocketMQ topic. Retention in RocketMQ is a broker-level setting (fileReservedTime / broker config), unlike Kafka where retention is a per-topic config, so the library deliberately fails fast instead of sending an unsupported key to the broker.

Source

Thrown at agents/drivers/rocketmq/topics.go:383

	}
	topicConfig, err := readTopicConfig(ctx, address, name)
	if err != nil {
		return nil, err
	}
	if entries, ok := params["configs"].([]any); ok {
		for _, raw := range entries {
			entry, _ := raw.(map[string]any)
			value := stringValue(entry, "value")
			switch stringValue(entry, "key") {
			case "readQueueNums":
				topicConfig.ReadQueueNums = intValue(entry, topicConfig.ReadQueueNums, "value")
			case "writeQueueNums":
				topicConfig.WriteQueueNums = intValue(entry, topicConfig.WriteQueueNums, "value")
			case "perm":
				topicConfig.Perm = normalizeTopicPerm(intValue(entry, topicConfig.Perm, "value"))
			case "retention.ms", "retention.bytes":
				if value != "" {
					return nil, fmt.Errorf("RocketMQ topic retention is broker-level and cannot be changed per topic")
				}
			}
		}
	}
	if err := writeTopicConfig(ctx, address, topicConfig); err != nil {
		return nil, err
	}
	return okResult(), nil
}

func (a *rocketMQAgent) skipTopicAccumulation(params map[string]any) (any, error) {
	topic, err := requireString(params, "topic", "name")
	if err != nil {
		return nil, err
	}
	client, config, _ := a.requireClient()
	ctx, cancel := context.WithTimeout(context.Background(), config.RequestTimeout)
	defer cancel()

View on GitHub (pinned to c0390bff16)

Solutions

  1. Remove the retention.ms/retention.bytes entries from the configs array and alter only readQueueNums, writeQueueNums, and perm
  2. Change retention at the broker level instead: set fileReservedTime (and related disk settings) in the broker's broker.conf and restart/roll the brokers
  3. Empty-string values for these keys are tolerated (no-op) — send the key with an empty value if the template must include it

Example fix

// before
configs: [
  {"key":"retention.ms","value":"259200000"},
  {"key":"writeQueueNums","value":"8"}
]
// after
configs: [
  {"key":"writeQueueNums","value":"8"}
]
// and set fileReservedTime=72 in broker.conf for broker-level retention
Defensive patterns

Strategy: validation

Validate before calling

var unsupported = map[string]bool{"retention.ms": true, "retention.bytes": true}
for _, e := range configs {
    if unsupported[e.Key] && e.Value != "" {
        return fmt.Errorf("drop %s: retention is broker-level in RocketMQ", e.Key)
    }
}

Prevention

When it happens

Trigger: Calling dispatch with an alter-topic-config action whose configs array contains an entry with key "retention.ms" or "retention.bytes" and a non-empty value. Only readQueueNums, writeQueueNums, and perm are mutable per topic.

Common situations: Porting Kafka automation scripts (topic-level retention.ttl.ms / retention.bytes) to RocketMQ; copy-pasted topic config templates including retention keys; IaC tooling that sets retention on every topic regardless of broker type.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/e32a7d0baa678363. Report an issue: GitHub.