{"id":"c3132b0f75cbad61","repo":"go-sql-driver/mysql","slug":"invalid-dsn-did-you-forget-to-escape-a-param-valu","errorCode":null,"errorMessage":"invalid DSN: did you forget to escape a param value?","messagePattern":"invalid DSN: did you forget to escape a param value\\?","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dsn.go","lineNumber":29,"sourceCode":"import (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"maps\"\n\t\"math/big\"\n\t\"net\"\n\t\"net/url\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar (\n\terrInvalidDSNUnescaped       = errors.New(\"invalid DSN: did you forget to escape a param value?\")\n\terrInvalidDSNAddr            = errors.New(\"invalid DSN: network address not terminated (missing closing brace)\")\n\terrInvalidDSNNoSlash         = errors.New(\"invalid DSN: missing the slash separating the database name\")\n\terrInvalidDSNUnsafeCollation = errors.New(\"invalid DSN: interpolateParams can not be used with unsafe collations\")\n)\n\n// Config is a configuration parsed from a DSN string.\n// If a new Config is created instead of being parsed from a DSN string,\n// the NewConfig function should be used, which sets default values.\ntype Config struct {\n\t// non boolean fields\n\n\tUser                 string            // Username\n\tPasswd               string            // Password (requires User)\n\tNet                  string            // Network (e.g. \"tcp\", \"tcp6\", \"unix\". default: \"tcp\")\n\tAddr                 string            // Address (default: \"127.0.0.1:3306\" for \"tcp\" and \"/tmp/mysql.sock\" for \"unix\")\n\tDBName               string            // Database name\n\tParams               map[string]string // Connection parameters\n\tConnectionAttributes string            // Connection Attributes, comma-delimited string of user-defined \"key:value\" pairs","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/go-sql-driver/mysql/blob/c426bd93799de0f0e094c8f0582872c529d0ed0a/dsn.go#L11-L47","documentation":"Returned by ParseDSN while parsing the [protocol(address)] section: it found an opening '(' but the segment between '(' and the '/' contains a stray ')' that is not the closing terminator (dsn.go:441). The hint asks whether a parameter value was left unescaped because, in practice, an unescaped ')' inside the password is what desynchronizes the parser and leaves a ')' in the address region.","triggerScenarios":"Calling ParseDSN or sql.Open('mysql', ...) with a DSN like 'user:p)ss(@tcp(host:3306)/db' where the raw password contains a ')' character before the '(' of the address. The parser, scanning dsn[k+1:i] for the address, sees the stray ')' via strings.ContainsRune and returns errInvalidDSNUnescaped.","commonSituations":"Passwords generated by a secret manager containing random punctuation; copy-pasting a DSN from a wiki that stripped URL-encoding; building the DSN with fmt.Sprintf and a raw password instead of url.QueryEscape; an OAUTH-style token used as a password.","solutions":["URL-encode the password (and username) when assembling the DSN: wrap each in url.QueryEscape(...).","Stop putting the secret in the DSN entirely: build a *mysql.Config, set cfg.User/cfg.Passwd, then open via mysql.NewConnector(cfg) + sql.OpenDB.","Audit the DSN for any literal '(' or ')' that is not part of the protocol(address) group and escape or remove it."],"exampleFix":"// before\ndsn := fmt.Sprintf(\"%s:%s@tcp(%s)/db\", user, pass, addr) // pass has a ')'\n// after\ndsn := fmt.Sprintf(\"%s:%s@tcp(%s)/db\", url.QueryEscape(user), url.QueryEscape(pass), addr)","handlingStrategy":"validation","validationCode":"// Escape user/password before building the DSN so ')' cannot desync the parser.\nfunc safeDSN(user, pass, addr, db string) string {\n    return fmt.Sprintf(\"%s:%s@tcp(%s)/%s\", url.QueryEscape(user), url.QueryEscape(pass), addr, db)\n}","typeGuard":null,"tryCatchPattern":"// errInvalidDSNUnescaped is unexported; match by message substring.\nif _, err := mysql.ParseDSN(dsn); err != nil {\n    if strings.Contains(err.Error(), \"forget to escape\") {\n        // re-escape credentials and rebuild the DSN\n    }\n}","preventionTips":["Never interpolate raw credentials into a DSN with fmt.Sprintf — always url.QueryEscape them.","Prefer building a *mysql.Config and opening via mysql.NewConnector to avoid string parsing entirely.","Add a unit test that ParseDSN succeeds for every DSN your config layer can produce."],"tags":["go","mysql","dsn","escaping","config"],"analyzedSha":"c426bd93799de0f0e094c8f0582872c529d0ed0a","analyzedAt":"2026-08-04T21:52:59.219Z","schemaVersion":2}