{"record":{"id":"6d802f91f608d406","repo":"JuliusBrussee/caveman","slug":"invalid-json-number-exponent","errorCode":null,"errorMessage":"invalid JSON number exponent","messagePattern":"invalid JSON number exponent","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/proposalrun/proposalrun.go","lineNumber":272,"sourceCode":"\t\t\treturn \"\", fmt.Errorf(\"JSON number exceeds PostgreSQL fraction-digit limit\")\n\t\t}\n\t\tout = \"0.\" + strings.Repeat(\"0\", scale-len(digits)) + digits\n\tdefault:\n\t\tif scale > maxJSONBNumericFractionDigits || len(digits)-scale > maxJSONBNumericIntegerDigits {\n\t\t\treturn \"\", fmt.Errorf(\"JSON number exceeds PostgreSQL numeric limits\")\n\t\t}\n\t\tpoint := len(digits) - scale\n\t\tout = digits[:point] + \".\" + digits[point:]\n\t}\n\tif negative {\n\t\treturn \"-\" + out, nil\n\t}\n\treturn out, nil\n}\n\nfunc parseJSONExponent(raw string) (int, error) {\n\tif len(raw) == 0 {\n\t\treturn 0, fmt.Errorf(\"invalid JSON number exponent\")\n\t}\n\tnegative := false\n\tif raw[0] == '+' || raw[0] == '-' {\n\t\tnegative = raw[0] == '-'\n\t\traw = raw[1:]\n\t}\n\tif len(raw) == 0 {\n\t\treturn 0, fmt.Errorf(\"invalid JSON number exponent\")\n\t}\n\tvalue := 0\n\tfor i := 0; i < len(raw); i++ {\n\t\tif raw[i] < '0' || raw[i] > '9' {\n\t\t\treturn 0, fmt.Errorf(\"invalid JSON number exponent\")\n\t\t}\n\t\tdigit := int(raw[i] - '0')\n\t\tif value > (maxJSONBNumberExponent-digit)/10 {\n\t\t\treturn 0, fmt.Errorf(\"JSON number exponent exceeds supported range\")\n\t\t}","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/proposalrun/proposalrun.go#L254-L290","documentation":"parseJSONExponent parses the text after 'e'/'E' in a JSON number literal. It throws when the exponent text is completely empty — i.e. the literal ends with 'e' or 'E' and no exponent digits follow. JSON requires at least one digit in the exponent, so the library rejects it during canonicalization rather than letting Postgres reject the malformed jsonb.","triggerScenarios":"canonicalizeJSONNumber -> normalizeJSONNumber on a literal such as 1e, 2.5E, or 1e+ where the string after the exponent marker is empty. Usually means the literal was hand-built or sliced incorrectly rather than produced by a real JSON parser.","commonSituations":"Hand-concatenating JSON numbers (e.g. mantissa + \"e\" + exponent from separate config values where the exponent is missing), string manipulation of serialized numbers, template-generated JSON with empty interpolation.","solutions":["Ensure the full number literal including exponent digits is passed in; do not split 'e' from its digits.","Validate the literal with a standard JSON parser (encoding/json) before canonicalizing.","If the exponent is genuinely absent, serialize the number without the exponent marker.","Log the raw literal that failed to find the malformed producer."],"exampleFix":"// before\nlit := \"1.5e\" + expStr // expStr may be \"\"\n// after\nif expStr != \"\" { lit = \"1.5e\" + expStr } else { lit = \"1.5\" }","handlingStrategy":"validation","validationCode":"var jsonNumRe = regexp.MustCompile(`^-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?$`)\nfunc validJSONNumber(lit string) bool { return jsonNumRe.MatchString(lit) }","typeGuard":null,"tryCatchPattern":"if !validJSONNumber(lit) {\n\treturn fmt.Errorf(\"malformed number literal %q\", lit)\n}","preventionTips":["Round-trip literals through encoding/json or strconv before canonicalizing","Never build number strings by concatenation without validating the result","Fail fast on the producer that emits a dangling 'e'"],"tags":["json","number-parsing","malformed-input"],"backgroundTag":"invalid-argument-format","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}