nats-io/nats-server · error · JSStreamInvalidConfigError

10052

10052

Error message

stream configuration for create can not be sealed

What it means

Sealed is a terminal stream state: once sealed, a stream is immutable and can never accept new data or configuration. Creating a new stream with Sealed already set in the configuration is contradictory, so the JetStream API rejects it with JSStreamInvalidConfigError (API error code 10052) rather than creating a permanently write-blocked stream.

Source

Thrown at server/jetstream_api.go:1515

	setStaticStreamMetadata(&cfg.StreamConfig)

	streamName := streamNameFromSubject(subject)
	if streamName != cfg.Name {
		resp.Error = NewJSStreamMismatchError()
		s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
		return
	}

	// Check for path like separators in the name.
	if strings.ContainsAny(streamName, `\/`) {
		resp.Error = NewJSStreamNameContainsPathSeparatorsError()
		s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
		return
	}

	// Can't create a stream with a sealed state.
	if cfg.Sealed {
		resp.Error = NewJSStreamInvalidConfigError(fmt.Errorf("stream configuration for create can not be sealed"))
		s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
		return
	}

	// If we are told to do mirror direct but are not mirroring, error.
	if cfg.MirrorDirect && cfg.Mirror == nil {
		resp.Error = NewJSStreamInvalidConfigError(fmt.Errorf("stream has no mirror but does have mirror direct"))
		s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
		return
	}

	// Hand off to cluster for processing.
	if s.JetStreamIsClustered() {
		s.jsClusteredStreamRequest(ci, acc, subject, reply, rmsg, &cfg)
		return
	}

	if err := acc.jsNonClusteredStreamLimitsCheck(&cfg.StreamConfig); err != nil {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Remove `sealed: true` from the create request config; seal the stream later with an update after creation.
  2. If cloning an existing sealed stream, strip the Sealed field before serializing the config.
  3. Use the stream restore API ($JS.API.STREAM.RESTORE) rather than create when reconstructing archived streams.

Example fix

// before
json.Marshal(&StreamConfig{Name: "x", Sealed: true}) // create rejected
// after
json.Marshal(&StreamConfig{Name: "x"}) // create, then seal via update if needed
Defensive patterns

Strategy: validation

Validate before calling

if cfg.Sealed {
    return errors.New("cannot create a stream with sealed=true; seal after creation via update")
}

Try / catch

// handle API error code 10052
if jserr != nil && jserr.ErrorCode == 10052 {
    // strip Sealed from config and retry create
}

Prevention

When it happens

Trigger: Sending STREAM CREATE ($JS.API.STREAM.CREATE.<name>) with a config JSON where "sealed": true.

Common situations: Restoring/backing up scripts that dump full stream state (including sealed) and replay it as a create request; template code that serializes a sealed source stream's config to clone it.

Related errors


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