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
- Remove `Sealed: true` from the stream config passed to the create call (leave it false/unset).
- If the goal is a sealed copy, first create the stream unsealed, then issue an update with Sealed set to true.
- 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
- Never copy Sealed from StreamInfo back into a create call.
- Seal streams via a separate update step after creation.
- Centralize stream creation in one helper that resets lifecycle fields.
- Document that Sealed is terminal and update-only.
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
- 10052
- 10069
- 10094
- max_request_batch must be set if it's JetStream limits are s
- consumer name can not contain '.', '*', '>', '\', '/'
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/4e459cdd9c9d942e.
Report an issue: GitHub.