{"id":"e674c43ac7ae2dd9","repo":"go-sql-driver/mysql","slug":"not-0-9","errorCode":null,"errorMessage":"not [0-9]","messagePattern":"not \\[0-9\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"utils.go","lineNumber":225,"sourceCode":"\nfunc parseByteNanoSec(b []byte) (int, error) {\n\tns, digit := 0, 100000 // max is 6-digits\n\tfor i := range b {\n\t\tv, err := bToi(b[i])\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tns += v * digit\n\t\tdigit /= 10\n\t}\n\t// nanoseconds has 10-digits. (needs to scale digits)\n\t// 10 - 6 = 4, so we have to multiple 1000.\n\treturn ns * 1000, nil\n}\n\nfunc bToi(b byte) (int, error) {\n\tif b < '0' || b > '9' {\n\t\treturn 0, errors.New(\"not [0-9]\")\n\t}\n\treturn int(b - '0'), nil\n}\n\nfunc parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) {\n\tswitch num {\n\tcase 0:\n\t\treturn time.Time{}, nil\n\tcase 4:\n\t\treturn time.Date(\n\t\t\tint(binary.LittleEndian.Uint16(data[:2])), // year\n\t\t\ttime.Month(data[2]),                       // month\n\t\t\tint(data[3]),                              // day\n\t\t\t0, 0, 0, 0,\n\t\t\tloc,\n\t\t), nil\n\tcase 7:\n\t\treturn time.Date(","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/go-sql-driver/mysql/blob/c426bd93799de0f0e094c8f0582872c529d0ed0a/utils.go#L207-L243","documentation":"Thrown by bToi (utils.go:225) — the lowest-level byte-to-digit helper — when a byte expected to be an ASCII digit [0-9] is not. It is the underlying cause behind parseByteYear, parseByte2Digits, and parseByteNanoSec, so a caller sees it as the error from any numeric field of a textual datetime (year, month, day, hour, minute, second, fractional digits).","triggerScenarios":"Parsing a textual datetime where one of the digit-group bytes is non-numeric — e.g. '20A0-01-01' (year has 'A'), '2020-0a-01' (month), or fractional '00:00:00.00000x'. Reachable from any time.Time scan of a DATE/DATETIME/TIMESTAMP column.","commonSituations":"Corrupted/partial bytes from a proxy or flaky network; a VARCHAR column containing alphanumeric garbage being scanned as time.Time; a non-MySQL server emitting non-digit characters; an encoding mismatch (UTF-8 multibyte char landing in a date field).","solutions":["The message itself is generic; enable verbose logging or SELECT CAST(col AS CHAR) to see the actual offending value.","Validate the column type — scan VARCHAR/CHAR date-like columns into a string and parse with time.Parse, handling its error explicitly.","Bypass any proxy/tunnel and reproduce against MySQL directly to confirm the server sends clean digits.","Clean the source data if it genuinely contains non-digit characters in date fields."],"exampleFix":"// before\nvar t time.Time\nerr := db.QueryRow(\"SELECT d FROM t\").Scan(&t)\n\n// after: parse defensively with a clear layout\nvar raw string\nerr := db.QueryRow(\"SELECT CAST(d AS CHAR) FROM t\").Scan(&raw)\nvar t time.Time\nif err == nil {\n    if t, err = time.ParseInLocation(\"2006-01-02\", raw, time.Local); err != nil {\n        // raw contains a non-digit; report it\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"var t time.Time\nif err := rows.Scan(&t); err != nil {\n    if err.Error() == \"not [0-9]\" || strings.Contains(err.Error(), \"not [0-9]\") {\n        // a non-digit byte was in the textual datetime; inspect via CAST(col AS CHAR)\n    } else {\n        return err\n    }\n}","preventionTips":["Don't scan alphanumeric or garbage VARCHAR data into time.Time; clean it first.","Read suspect columns as a string and validate with a regexp like ^[0-9]{4}-[0-9]{2}-[0-9]{2} before parsing.","Keep the wire path clean of proxies that corrupt bytes."],"tags":["datetime","parsing","numeric","wire-format"],"analyzedSha":"c426bd93799de0f0e094c8f0582872c529d0ed0a","analyzedAt":"2026-08-04T21:52:59.219Z","schemaVersion":2}