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
- Reduce metadata size: shorten keys and values, drop unneeded entries.
- Move large blobs out of metadata into an external store and keep a reference.
- Compute total size client-side (sum of len(k)+len(v)) and enforce <= 131072 before the API call.
- 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
- Measure total key+value bytes before submitting metadata.
- Keep large payloads out of metadata; store references instead.
- Trim keys as well as values — both count toward the limit.
- Re-check limits after any templated/config-driven metadata injection.
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
- JS_STREAM_MSG_DELETE_FAILED
- JS_STREAM_PURGE_FAILED
- JS_STREAM_ROLLUP_FAILED
- stream republish transform from '%s' to '%s': %w
- stream configuration invalid
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/4c0d347744779ee3.
Report an issue: GitHub.