{"record":{"id":"493626e457cb2e27","repo":"nats-io/nats-server","slug":"must-be-int64-or-string","errorCode":null,"errorMessage":"must be int64 or string","messagePattern":"must be int64 or string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/opts.go","lineNumber":2503,"sourceCode":"\t\t\t}\n\t\t}\n\t\tacc.jsLimits = map[string]JetStreamAccountLimits{_EMPTY_: jsLimits}\n\tdefault:\n\t\treturn &configErr{tk, fmt.Sprintf(\"Expected map, bool or string to define JetStream, got %T\", v)}\n\t}\n\treturn nil\n}\n\n// takes in a storage size as either an int or a string and returns an int64 value based on the input.\nfunc getStorageSize(v any) (int64, error) {\n\t_, ok := v.(int64)\n\tif ok {\n\t\treturn v.(int64), nil\n\t}\n\n\ts, ok := v.(string)\n\tif !ok {\n\t\treturn 0, fmt.Errorf(\"must be int64 or string\")\n\t}\n\n\tif s == _EMPTY_ {\n\t\treturn 0, nil\n\t}\n\n\tsuffix := s[len(s)-1:]\n\tprefix := s[:len(s)-1]\n\tnum, err := strconv.ParseInt(prefix, 10, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tsuffixMap := map[string]int64{\"K\": 10, \"M\": 20, \"G\": 30, \"T\": 40}\n\n\tmult, ok := suffixMap[suffix]\n\tif !ok {\n\t\treturn 0, fmt.Errorf(\"sizes defined as strings must end in K, M, G, T\")","sourceCodeStart":2485,"sourceCodeEnd":2521,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/opts.go#L2485-L2521","documentation":"getStorageSize() in nats-server (server/opts.go:2495) parses a JetStream storage size that config may express as either an int64 (bytes) or a string with a size suffix. If the value is neither an int64 nor a string, the function returns 'must be int64 or string'. This protects against wrong-typed config values (e.g. YAML/JSON numbers decoded as float64 or bool).","triggerScenarios":"Passing a value of any type other than int64 or string to getStorageSize, e.g. a JSON number decoded as float64/float32, a bool, a map, or an int (not int64) in parsed config used for JetStream limits (parseJetStreamLimits path).","commonSituations":"Writing a size as a plain YAML/JSON number that decodes to float64 instead of int64; using an int literal in Go config-building code instead of int64; feeding config from a generic map[string]any without normalizing types.","solutions":["Convert the value to int64 (bytes) or a string like \"1G\" before passing it","If the value comes from JSON/YAML generic decoding, convert float64 to int64 first (int64(v.(float64)))","Check the config section producing the value and fix its type","Upgrade/patch config parsing so numeric values are decoded as int64"],"exampleFix":"// before\nlimits[\"max_memory\"] = 1024 // decoded as int or float64, not int64\n// after\nlimits[\"max_memory\"] = int64(1024)\n// or as a string\nlimits[\"max_memory\"] = \"1K\"","handlingStrategy":"type-guard","validationCode":"func validStorageSize(v any) bool {\n\tswitch t := v.(type) {\n\tcase int64:\n\t\treturn t >= 0\n\tcase string:\n\t\tif t == \"\" { return true }\n\t\ts := t[:len(t)-1]\n\t\t_, err := strconv.ParseInt(s, 10, 64)\n\t\treturn err == nil && strings.ContainsAny(t[len(t)-1:], \"KMGT\")\n\t}\n\treturn false\n}","typeGuard":"func asStorageSize(v any) (int64, bool) {\n\tswitch t := v.(type) {\n\tcase int64:\n\t\treturn t, true\n\tcase int:\n\t\treturn int64(t), true\n\tcase float64:\n\t\treturn int64(t), true\n\t}\n\treturn 0, false\n}","tryCatchPattern":"if size, err := getStorageSize(v); err != nil {\n\treturn fmt.Errorf(\"storage size invalid: %w\", err)\n}","preventionTips":["Always use int64 for byte counts in Go config builders","Normalize JSON/YAML-decoded float64 values to int64 before validation","Prefer string sizes with explicit units (\"1G\") to avoid type ambiguity"],"tags":["config","jetstream","type-mismatch"],"backgroundTag":"invalid-config-value-type","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}