{"record":{"id":"3bf1440ea6850f56","repo":"nats-io/nats-server","slug":"stream-configuration-invalid","errorCode":null,"errorMessage":"stream configuration invalid","messagePattern":"stream configuration invalid","errorType":"error_code","errorClass":"JSStreamInvalidConfigError","httpStatus":null,"severity":"error","filePath":"server/stream.go","lineNumber":1871,"sourceCode":"\n// StreamDefaultDuplicatesWindow default duplicates window.\nconst StreamDefaultDuplicatesWindow = 2 * time.Minute\n\n// Do not hold the jetStream lock, it will be read-locked internally.\nfunc (s *Server) checkStreamCfg(config *StreamConfig, acc *Account, pedantic bool) (StreamConfig, *ApiError) {\n\tif js := s.getJetStream(); js != nil {\n\t\tjs.mu.RLock()\n\t\tdefer js.mu.RUnlock()\n\t}\n\treturn s.checkStreamCfgLocked(config, acc, pedantic)\n}\n\n// jetStream lock (read or write) should be held, if JetStream is enabled.\nfunc (s *Server) checkStreamCfgLocked(config *StreamConfig, acc *Account, pedantic bool) (StreamConfig, *ApiError) {\n\tlim := &s.getOpts().JetStreamLimits\n\n\tif config == nil {\n\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"stream configuration invalid\"))\n\t}\n\tif !isValidAssetName(config.Name) {\n\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"stream name is required and can not contain '.', '*', '>', '\\\\', '/'\"))\n\t}\n\tif len(config.Name) > JSMaxNameLen {\n\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"stream name is too long, maximum allowed is %d\", JSMaxNameLen))\n\t}\n\tif len(config.Description) > JSMaxDescriptionLen {\n\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"stream description is too long, maximum allowed is %d\", JSMaxDescriptionLen))\n\t}\n\n\tvar metadataLen int\n\tfor k, v := range config.Metadata {\n\t\tmetadataLen += len(k) + len(v)\n\t}\n\tif metadataLen > JSMaxMetadataLen {\n\t\treturn StreamConfig{}, NewJSStreamInvalidConfigError(fmt.Errorf(\"stream metadata exceeds maximum size of %d bytes\", JSMaxMetadataLen))\n\t}","sourceCodeStart":1853,"sourceCodeEnd":1889,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/stream.go#L1853-L1889","documentation":"checkStreamCfgLocked rejects the whole stream config because it was nil. The JetStream API requires a non-nil StreamConfig to create or update a stream. This is the first guard in config validation and returns a JSStreamInvalidConfigError surfaced as API error 'stream configuration invalid'.","triggerScenarios":"Issuing a JS API STREAM.CREATE or STREAM.UPDATE request whose 'config' field is missing or null; calling AddStream/Add/update with a nil *StreamConfig.","commonSituations":"Hand-crafted API request JSON omitting the config object; deserialization failure upstream leaving config nil; calling low-level server APIs directly instead of a client library.","solutions":["Provide a complete StreamConfig in the request (at minimum Name, Subjects, Storage, Replicas).","Check the JSON payload you send includes the 'config' key with an object.","Use a maintained client (nats.go / jetstream package) that always serializes config.","Log the request body when this occurs to confirm config is absent."],"exampleFix":"// before\nnc.Request(fmt.Sprintf(JSApiStreamCreateT, name), nil, time.Second)\n// after\nreq := &jetstream.StreamConfig{Name: name, Subjects: []string{\"foo\"}, Storage: jetstream.FileStorage}\nbody, _ := json.Marshal(req)\nnc.Request(fmt.Sprintf(JSApiStreamCreateT, name), body, time.Second)","handlingStrategy":"validation","validationCode":"func requireConfig(cfg *jetstream.StreamConfig) error {\n  if cfg == nil { return errors.New(\"stream config must not be nil\") }\n  if cfg.Name == \"\" { return errors.New(\"stream name required\") }\n  return nil\n}","typeGuard":"func hasStreamConfig(cfg *jetstream.StreamConfig) bool { return cfg != nil && cfg.Name != \"\" }","tryCatchPattern":"cfg, err := js.GetStream(ctx, name)\nif errors.Is(err, jetstream.ErrStreamNotFound) || cfg == nil {\n  // config was missing/invalid: build and resubmit a full StreamConfig\n}","preventionTips":["Always construct StreamConfig via a single helper instead of ad-hoc literals.","Never send nil/omitted config in raw JS API requests.","Round-trip configs through json.Marshal in tests to catch missing fields.","Use maintained client libraries instead of hand-crafted API requests.","Log the outgoing request body when stream creation fails."],"tags":["jetstream","stream-config","nil-config","api"],"backgroundTag":"invalid-stream-config","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}