nats-io/nats-server · error

failed to update stream config: %w

Error message

failed to update stream config: %w

What it means

The retained messages stream existed but its subjects did not match the expected single $MQTT.rmsgs subject, so the server attempted a stream update via updateStream; that update failed and the underlying JetStream error is wrapped here. It signals a stream config mismatch that could not be repaired automatically.

Source

Thrown at server/mqtt.go:1535

		}
		needToTransfer = false

	default:
		needToTransfer = si.Config.MaxMsgsPer != 1
	}
	// Guard before dereferencing si.Config below.
	if si == nil {
		return nil, fmt.Errorf("could not look up or create the retained messages stream for account %q", accName)
	}

	// Doing this check outside of above if/else due to possible race when
	// creating the stream.
	wantedSubj := mqttRetainedMsgsStreamSubject + ">"
	if len(si.Config.Subjects) != 1 || si.Config.Subjects[0] != wantedSubj {
		// Update only the Subjects at this stage, not MaxMsgsPer yet.
		si.Config.Subjects = []string{wantedSubj}
		if si, err = jsa.updateStream(&si.Config); err != nil {
			return nil, fmt.Errorf("failed to update stream config: %w", err)
		}
	}

	transferRMS := func() error {
		if !needToTransfer {
			return nil
		}

		as.transferRetainedToPerKeySubjectStream(s)

		// We need another lookup to have up-to-date si.State values in order
		// to load all retained messages.
		si, err = lookupStream(mqttRetainedMsgsStreamName, "retained messages")
		if err != nil {
			return err
		}
		needToTransfer = false
		return nil

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Delete the existing retained messages stream ($MQTT.rmsgs related) while no MQTT clients are connected and let the server recreate it
  2. Compare the stream's config with what the server expects and correct subjects manually via nats stream update
  3. Check server logs for the wrapped update error (e.g. invalid config change, no responders) and address it
  4. Run all server nodes on the same NATS Server version to avoid config schema mismatches

Example fix

// before
nats stream info $MQTT.rmsgs   # subjects: ["foo"]
// after
nats stream rm $MQTT.rmsgs --force
# restart MQTT clients; server recreates stream with subject $MQTT.rmsgs.>
Defensive patterns

Strategy: retry

Validate before calling

// Verify the stream config matches what the server expects:
js, _ := nc.JetStream()
si, err := js.StreamInfo("$MQTT.rmsgs")
if err == nil {
    subjects := si.Config.Subjects
    if len(subjects) != 1 || subjects[0] != "$MQTT.rmsgs.>" {
        // mismatch: delete stream during maintenance so server recreates it
    }
}

Try / catch

if err != nil && strings.Contains(err.Error(), "failed to update stream config") {
    log.Printf("retained stream config mismatch: %v", err)
    // schedule maintenance deletion: nats stream rm $MQTT.rmsgs --force
}

Prevention

When it happens

Trigger: An existing stream with the retained-messages name was created manually or by an older server version with different subjects; updateStream's JS API request fails (invalid config change, no permission, timeout, resources).

Common situations: Upgrading from an older NATS server whose MQTT retained stream used a different subject config; operator manually created/edited the stream; JetStream API errors like invalid stream configuration during update.

Related errors


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