{"record":{"id":"9e0bf3227af15961","repo":"go-sql-driver/mysql","slug":"key-s-is-reserved","errorCode":null,"errorMessage":"key '%s' is reserved","messagePattern":"key '(.+?)' is reserved","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"utils.go","lineNumber":59,"sourceCode":"//\t    log.Fatal(err)\n//\t}\n//\tif ok := rootCertPool.AppendCertsFromPEM(pem); !ok {\n//\t    log.Fatal(\"Failed to append PEM.\")\n//\t}\n//\tclientCert := make([]tls.Certificate, 0, 1)\n//\tcerts, err := tls.LoadX509KeyPair(\"/path/client-cert.pem\", \"/path/client-key.pem\")\n//\tif err != nil {\n//\t    log.Fatal(err)\n//\t}\n//\tclientCert = append(clientCert, certs)\n//\tmysql.RegisterTLSConfig(\"custom\", &tls.Config{\n//\t    RootCAs: rootCertPool,\n//\t    Certificates: clientCert,\n//\t})\n//\tdb, err := sql.Open(\"mysql\", \"user@tcp(localhost:3306)/test?tls=custom\")\nfunc RegisterTLSConfig(key string, config *tls.Config) error {\n\tif _, isBool := readBool(key); isBool || strings.ToLower(key) == \"skip-verify\" || strings.ToLower(key) == \"preferred\" {\n\t\treturn fmt.Errorf(\"key '%s' is reserved\", key)\n\t}\n\n\ttlsConfigLock.Lock()\n\tif tlsConfigRegistry == nil {\n\t\ttlsConfigRegistry = make(map[string]*tls.Config)\n\t}\n\n\ttlsConfigRegistry[key] = config\n\ttlsConfigLock.Unlock()\n\treturn nil\n}\n\n// DeregisterTLSConfig removes the tls.Config associated with key.\nfunc DeregisterTLSConfig(key string) {\n\ttlsConfigLock.Lock()\n\tif tlsConfigRegistry != nil {\n\t\tdelete(tlsConfigRegistry, key)\n\t}","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/go-sql-driver/mysql/blob/03d76c7e07908e255ce62d126d07ede3f2365d86/utils.go#L41-L77","documentation":"RegisterTLSConfig rejects key names that collide with the DSN 'tls=' shorthand: bool-parseable values (true/false/1/0) and the literals 'skip-verify' and 'preferred' (case-insensitive). These names are reserved so the DSN parser can distinguish them from custom configs.","triggerScenarios":"Calling mysql.RegisterTLSConfig(\"skip-verify\", cfg), RegisterTLSConfig(\"preferred\", cfg), or RegisterTLSConfig(\"true\"/\"false\"/\"1\"/\"0\", cfg).","commonSituations":"Choosing a config name that happens to be one of the reserved literals; auto-generating a name that lands on a reserved value.","solutions":["Pick a non-reserved name for your TLS config, e.g. 'custom', 'prod-tls', or 'rds'.","If you wanted the reserved behavior, use the literal directly in the DSN (?tls=true / skip-verify / preferred) instead of registering a config.","Update the DSN's tls= parameter to match the new non-reserved name."],"exampleFix":"// before\nmysql.RegisterTLSConfig(\"skip-verify\", tlsCfg) // -> reserved\n// after\nmysql.RegisterTLSConfig(\"custom\", tlsCfg)\nsql.Open(\"mysql\", \"user@tcp(host:3306)/db?tls=custom\")","handlingStrategy":"validation","validationCode":"// Reject reserved TLS config names before registering.\nfunc notReservedName(name string) bool {\n    if _, isBool := readBool(name); isBool { return false }\n    switch strings.ToLower(name) {\n    case \"skip-verify\", \"preferred\": return false\n    }\n    return true\n}","typeGuard":"null","tryCatchPattern":"// RegisterTLSConfig returns the error directly.\nif err := mysql.RegisterTLSConfig(name, tlsCfg); err != nil {\n    if strings.Contains(err.Error(), \"is reserved\") {\n        name = \"custom\" // pick a non-reserved name\n    }\n}","preventionTips":["Avoid bool-like and skip-verify/preferred names for custom TLS configs.","Use the DSN literals for built-in TLS behaviors instead of registering.","Guard registration against reserved names in a helper."],"tags":["go","mysql","tls","config","security"],"backgroundTag":null,"analyzedSha":"03d76c7e07908e255ce62d126d07ede3f2365d86","analyzedAt":"2026-08-07T10:39:17.340Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}