{"record":{"id":"6ed8e3cb1ed55f3c","repo":"nats-io/nats-server","slug":"could-not-look-up-or-create-the-retained-messages","errorCode":null,"errorMessage":"could not look up or create the retained messages stream for account %q","messagePattern":"could not look up or create the retained messages stream for account %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mqtt.go","lineNumber":1525,"sourceCode":"\t\tif err != nil {\n\t\t\tif isErrorOtherThan(err, JSStreamNameExistErr) {\n\t\t\t\treturn nil, fmt.Errorf(\"create retained messages stream for account %q: %v\", accName, err)\n\t\t\t}\n\t\t\t// Suppose we had a race and the stream was actually created by another\n\t\t\t// node, we really need \"si\" after that, so lookup the stream again here.\n\t\t\tsi, err = lookupStream(mqttRetainedMsgsStreamName, \"retained messages\")\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tneedToTransfer = false\n\n\tdefault:\n\t\tneedToTransfer = si.Config.MaxMsgsPer != 1\n\t}\n\t// Guard before dereferencing si.Config below.\n\tif si == nil {\n\t\treturn nil, fmt.Errorf(\"could not look up or create the retained messages stream for account %q\", accName)\n\t}\n\n\t// Doing this check outside of above if/else due to possible race when\n\t// creating the stream.\n\twantedSubj := mqttRetainedMsgsStreamSubject + \">\"\n\tif len(si.Config.Subjects) != 1 || si.Config.Subjects[0] != wantedSubj {\n\t\t// Update only the Subjects at this stage, not MaxMsgsPer yet.\n\t\tsi.Config.Subjects = []string{wantedSubj}\n\t\tif si, err = jsa.updateStream(&si.Config); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to update stream config: %w\", err)\n\t\t}\n\t}\n\n\ttransferRMS := func() error {\n\t\tif !needToTransfer {\n\t\t\treturn nil\n\t\t}\n","sourceCodeStart":1507,"sourceCodeEnd":1543,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/mqtt.go#L1507-L1543","documentation":"After attempting to create or look up the retained messages stream, the server found its StreamInfo pointer nil, meaning neither creation nor lookup produced a usable stream. This is a defensive guard against a race where createStream returned 'already exists' but the subsequent lookupStream also failed silently in the code path, leaving no stream info to dereference.","triggerScenarios":"A race between server nodes: one node's createStream hits JSStreamNameExistErr, then the follow-up lookupStream fails/returns nil so si remains nil when the code proceeds to read si.Config; essentially stream neither created nor found.","commonSituations":"Clustered NATS servers racing to serve MQTT clients for the same account simultaneously; JetStream meta-layer inconsistency after a cluster split or restart; transient JS API failures during lookup.","solutions":["Retry the MQTT connection once the cluster is stable; the guard is defensive and transient races usually resolve","Inspect JetStream stream list for the retained messages stream and delete a half-created/conflicting stream, then reconnect","Check cluster health (jetstream cluster state) and repair any meta-layer issues before reconnecting MQTT clients","Update to a recent NATS server version; this race handling has been hardened over releases"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Ensure stream is discoverable before MQTT use:\njs, _ := nc.JetStream()\n_, err := js.StreamInfo(\"$MQTT.rmsgs\")\n// err == ErrStreamNotFound is fine pre-connect; anything else investigate","typeGuard":"func hasStreamInfo(si *nats.StreamInfo) bool { return si != nil && si.Config.Name != \"\" }","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"could not look up or create the retained messages stream\") {\n    // transient race in cluster; retry after delay\n    time.Sleep(2 * time.Second)\n    return reconnectMQTT()\n}","preventionTips":["Avoid simultaneous first-time MQTT connects across many clients in a fresh cluster; stagger them","Keep the cluster meta layer healthy (odd number of JS peers)","Do not delete internal $MQTT streams while clients are connected"],"tags":["jetstream","mqtt","race-condition","stream-lookup"],"backgroundTag":"jetstream-stream-not-found","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}