{"id":"6caa65aeb686b798","repo":"go-sql-driver/mysql","slug":"invalid-dbname-q-w","errorCode":null,"errorMessage":"invalid dbname %q: %w","messagePattern":"invalid dbname %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dsn.go","lineNumber":465,"sourceCode":"\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcfg.Net = dsn[j+1 : k]\n\t\t\t}\n\n\t\t\t// dbname[?param1=value1&...&paramN=valueN]\n\t\t\t// Find the first '?' in dsn[i+1:]\n\t\t\tfor j = i + 1; j < len(dsn); j++ {\n\t\t\t\tif dsn[j] == '?' {\n\t\t\t\t\tif err = parseDSNParams(cfg, dsn[j+1:]); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdbname := dsn[i+1 : j]\n\t\t\tif cfg.DBName, err = url.PathUnescape(dbname); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid dbname %q: %w\", dbname, err)\n\t\t\t}\n\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif !foundSlash && len(dsn) > 0 {\n\t\treturn nil, errInvalidDSNNoSlash\n\t}\n\n\tif err = cfg.normalize(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn\n}\n\n// parseDSNParams parses the DSN \"query string\"\n// Values must be url.QueryEscape'ed","sourceCodeStart":447,"sourceCodeEnd":483,"githubUrl":"https://github.com/go-sql-driver/mysql/blob/c426bd93799de0f0e094c8f0582872c529d0ed0a/dsn.go#L447-L483","documentation":"After isolating the database-name segment (between '/' and '?'), ParseDSN runs url.PathUnescape on it. If the segment contains malformed percent-encoding (e.g. '%ZZ', a lone '%' with no hex digits, or a truncated '%2'), PathUnescape fails and the driver wraps the underlying error as 'invalid dbname %q: %w' at dsn.go:465.","triggerScenarios":"A DSN like 'user@tcp(host:3306)/my%ZZdb' or 'user@tcp(host:3306)/db%' where the dbname portion is not valid percent-encoding; a literal '%' in the name not encoded as '%25'.","commonSituations":"A database name containing a literal '%' or other special char that someone half-escaped; a templating system injected a raw symbol; URL-fragment accidentally included.","solutions":["URL-encode the database name with url.PathEscape when constructing the DSN.","If the name genuinely contains '%', encode it as '%25'.","Set cfg.DBName directly on a *mysql.Config and open via mysql.NewConnector(cfg) to bypass DSN parsing entirely."],"exampleFix":"// before\ndsn := fmt.Sprintf(\"user@tcp(host:3306)/%s\", dbname) // dbname has '%'\n// after\ndsn := fmt.Sprintf(\"user@tcp(host:3306)/%s\", url.PathEscape(dbname))","handlingStrategy":"validation","validationCode":"// Confirm the dbname segment round-trips through PathUnescape.\nfunc validDBNameSegment(seg string) bool {\n    _, err := url.PathUnescape(seg)\n    return err == nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := mysql.ParseDSN(dsn); err != nil {\n    var ue *url.EscapeError\n    if errors.As(err, &ue) || strings.Contains(err.Error(), \"invalid dbname\") {\n        // re-encode the dbname with url.PathEscape and rebuild\n    }\n}","preventionTips":["Always url.PathEscape the database name when building a DSN.","Set cfg.DBName directly and open via mysql.NewConnector to skip string parsing.","Validate DSNs from env vars in a config loader before they reach sql.Open."],"tags":["go","mysql","dsn","url-encoding","config"],"analyzedSha":"c426bd93799de0f0e094c8f0582872c529d0ed0a","analyzedAt":"2026-08-04T21:52:59.219Z","schemaVersion":2}