nats-io/nats-server · error · JSStreamInvalidConfigError

10052

10052

Error message

stream configuration for create can not be sealed

What it means

This is JSStreamInvalidConfigError (API error 10052) returned by the stream CREATE API handler when the submitted stream configuration has Sealed set to true. Sealed is a terminal state that can only be reached by updating an existing stream (sealing it after it has data); a brand-new stream cannot be created already sealed, so creation is rejected.

Source

Thrown at server/jetstream_cluster.go:10086

			currentIName[s.iname] = struct{}{}
		}
		for _, s := range cfg.Sources {
			s.setIndexName()
			if _, ok := currentIName[s.iname]; !ok {
				s.iname = _EMPTY_
			}
		}
		if !reflect.DeepEqual(osa.Config, cfg) {
			resp.Error = NewJSStreamNameExistError()
			s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp))
			return
		}
		// This is an equal assignment.
		self, rg, syncSubject = osa, osa.Group, osa.Sync
	}

	if cfg.Sealed {
		resp.Error = NewJSStreamInvalidConfigError(fmt.Errorf("stream configuration for create can not be sealed"))
		s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp))
		return
	}

	// Check for subject collisions here.
	if js.subjectsOverlap(acc.Name, cfg.Subjects, self) {
		resp.Error = NewJSStreamSubjectOverlapError()
		s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp))
		return
	}

	apiErr = js.jsClusteredStreamLimitsCheck(acc, cfg)
	// Check for stream limits here before proposing. These need to be tracked from meta layer, not jsa.
	if apiErr != nil {
		resp.Error = apiErr
		s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp))
		return
	}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Remove `Sealed: true` from the stream config passed to the create call (leave it false/unset).
  2. If the goal is a sealed copy, first create the stream unsealed, then issue an update with Sealed set to true.
  3. When cloning a stream from StreamInfo, explicitly reset lifecycle fields (Sealed) before creating.

Example fix

// before
cfg := si.Config // Sealed==true copied from existing stream
js.CreateStream(nc, cfg)
// after
cfg := si.Config
cfg.Sealed = false
js.CreateStream(nc, cfg)
Defensive patterns

Strategy: validation

Validate before calling

// Go client: sanitize config before create
if cfg.Sealed {
    cfg.Sealed = false // sealed streams cannot be created; seal after creation
}
js.CreateStream(nc, cfg)

Type guard

func isValidCreateConfig(cfg *nats.StreamConfig) bool {
    return cfg != nil && !cfg.Sealed
}

Try / catch

_, err := js.CreateStream(nc, cfg)
var apiErr *nats.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode == 10052 {
    cfg.Sealed = false
    _, err = js.CreateStream(nc, cfg)
}

Prevention

When it happens

Trigger: Calling the `$JS.API.STREAM.CREATE.*` subject or `jetstream.CreateStream`/`js.AddStream` with `Config.Sealed: true`, or via `nats stream add --seal` equivalent on a new stream.

Common situations: Scripts that snapshot a stream's config (including Sealed=true from a `StreamInfo`) and feed that config back into a create call when recreating or migrating streams.

Related errors


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