{"record":{"id":"82c608c786639fab","repo":"vitessio/vitess","slug":"can-t-convert-q-to-decimal-too-short","errorCode":null,"errorMessage":"can't convert %q to decimal: too short","messagePattern":"can't convert %q to decimal: too short","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/mysql/decimal/scan.go","lineNumber":104,"sourceCode":"\treturn int32(totalLen - 1), int32(totalLen - 1 - idx)\n}\n\nfunc NewFromMySQL(s []byte) (Decimal, error) {\n\toriginal := s\n\tvar neg bool\n\n\tif len(s) > 0 {\n\t\tswitch s[0] {\n\t\tcase '+':\n\t\t\ts = s[1:]\n\t\tcase '-':\n\t\t\tneg = true\n\t\t\ts = s[1:]\n\t\t}\n\t}\n\n\tif len(s) == 0 {\n\t\treturn Decimal{}, fmt.Errorf(\"can't convert %q to decimal: too short\", original)\n\t}\n\n\tif len(s) <= 18 {\n\t\tdec, err := parseDecimal64(s)\n\t\tif err == nil {\n\t\t\tif neg {\n\t\t\t\tdec.value.Neg(dec.value)\n\t\t\t}\n\t\t\treturn dec, nil\n\t\t}\n\t\tif err != errOverflow {\n\t\t\treturn Decimal{}, fmt.Errorf(\"can't convert %s to decimal: %v\", original, err)\n\t\t}\n\t}\n\n\tvar fractional, integral []byte\n\tif pIndex := bytes.IndexByte(s, '.'); pIndex >= 0 {\n\t\tif bytes.IndexByte(s[pIndex+1:], '.') != -1 {","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/mysql/decimal/scan.go#L86-L122","documentation":"NewFromMySQL rejects an empty decimal payload. After an optional leading sign is stripped, if no characters remain there is nothing to parse, so it returns this error quoting the original input. This guards against empty binary strings or a bare sign like \"-\".","triggerScenarios":"Calling NewFromMySQL with a zero-length byte slice, or a slice containing only a '-' or '+' sign (the sign is stripped, leaving len(s)==0).","commonSituations":"A column value truncated to empty by a misbehaving sender; hand-written serialization code writing a sign but no digits; fuzz tests or adversarial clients feeding empty DECIMAL payloads.","solutions":["Check the caller and fix the source so it never produces an empty or sign-only decimal payload.","Add a length check on the wire data before calling NewFromMySQL and handle empty values explicitly (e.g. treat as NULL or zero).","If a zero value is acceptable for empty input, default to NewFromMySQL([]byte(\"0\")) at the call site."],"exampleFix":"// before\ndec, err := NewFromMySQL(data)\n\n// after\nif len(bytes.Trim(data, \"+-\")) == 0 {\n    dec, err = NewFromMySQL([]byte(\"0\"))\n} else {\n    dec, err = NewFromMySQL(data)\n}","handlingStrategy":"validation","validationCode":"if len(bytes.Trim(data, \"+-\")) == 0 {\n    return fmt.Errorf(\"refusing to parse empty decimal payload\")\n}","typeGuard":null,"tryCatchPattern":"dec, err := decimal.NewFromMySQL(data)\nif err != nil {\n    if strings.Contains(err.Error(), \"too short\") {\n        dec, err = decimal.NewFromMySQL([]byte(\"0\")) // or treat as NULL\n    }\n}","preventionTips":["Check for empty or sign-only payloads at the deserialization boundary.","Treat empty DECIMAL wire data as NULL or zero explicitly per your domain semantics.","Fix senders that truncate values to zero-length.","Cover the empty-input case in unit tests for your decimal handling code."],"tags":["mysql","decimal","empty-input","parsing"],"backgroundTag":"invalid-decimal-string","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}