nats-io/nats-server · error · JSStreamInvalidConfigError

stream metadata exceeds maximum size of %d bytes

Error message

stream metadata exceeds maximum size of %d bytes

What it means

The combined length of all keys and values in StreamConfig.Metadata exceeds JSMaxMetadataLen (128 KiB, 131072 bytes). The server sums len(k)+len(v) across the map and rejects configs over this budget to bound config size.

Source

Thrown at server/stream.go:1888

	if config == nil {
		return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("stream configuration invalid"))
	}
	if !isValidAssetName(config.Name) {
		return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("stream name is required and can not contain '.', '*', '>', '\\', '/'"))
	}
	if len(config.Name) > JSMaxNameLen {
		return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("stream name is too long, maximum allowed is %d", JSMaxNameLen))
	}
	if len(config.Description) > JSMaxDescriptionLen {
		return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("stream description is too long, maximum allowed is %d", JSMaxDescriptionLen))
	}

	var metadataLen int
	for k, v := range config.Metadata {
		metadataLen += len(k) + len(v)
	}
	if metadataLen > JSMaxMetadataLen {
		return StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf("stream metadata exceeds maximum size of %d bytes", JSMaxMetadataLen))
	}

	cfg := *config

	if _, err := cfg.Retention.MarshalJSON(); err != nil {
		return cfg, NewJSStreamInvalidConfigError(fmt.Errorf("invalid retention"))
	}
	if _, err := cfg.Discard.MarshalJSON(); err != nil {
		return cfg, NewJSStreamInvalidConfigError(fmt.Errorf("invalid discard policy"))
	}
	if _, err := cfg.Compression.MarshalJSON(); err != nil {
		return cfg, NewJSStreamInvalidConfigError(fmt.Errorf("invalid compression"))
	}

	// Make file the default.
	if cfg.Storage == 0 {
		cfg.Storage = FileStorage
	}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Reduce metadata size: shorten keys and values, drop unneeded entries.
  2. Move large blobs out of metadata into an external store and keep a reference.
  3. Compute total size client-side (sum of len(k)+len(v)) and enforce <= 131072 before the API call.
  4. Apply the same budget to consumer metadata, which shares the same limit.

Example fix

// before
md := map[string]string{"doc": largeJSON} // > 128 KiB
js.CreateStream(ctx, jetstream.StreamConfig{Name: "ORDERS", Metadata: md})
// after
md := map[string]string{"docRef": "s3://bucket/doc.json"}
js.CreateStream(ctx, jetstream.StreamConfig{Name: "ORDERS", Metadata: md})
Defensive patterns

Strategy: validation

Validate before calling

func validMetadata(md map[string]string) error {
  var n int
  for k, v := range md { n += len(k) + len(v) }
  if n > 128*1024 {
    return fmt.Errorf("metadata too large: %d > %d bytes", n, 128*1024)
  }
  return nil
}

Type guard

func metadataFits(md map[string]string) bool {
  n := 0
  for k, v := range md { n += len(k) + len(v) }
  return n <= 128*1024
}

Prevention

When it happens

Trigger: Creating/updating a stream with a Metadata map whose total key+value bytes exceed 131072; attaching large JSON documents, traces, or template-generated metadata to the stream.

Common situations: Sync tools that copy entire resource tags into stream metadata; CI injecting verbose labels; aggregating per-tenant metadata into one stream's map.

Related errors


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