{"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":"error","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/c426bd93799de0f0e094c8f0582872c529d0ed0a/utils.go#L41-L77","documentation":"Returned by mysql.RegisterTLSConfig (utils.go:57-59) when the requested key collides with a reserved value the DSN parser treats specially. The parser interprets tls=true/false, tls=skip-verify, and tls=preferred as built-in modes, so registering a config under any of those names would be ambiguous and is rejected.","triggerScenarios":"Calling mysql.RegisterTLSConfig with key equal to one of: \"1\",\"true\",\"TRUE\",\"True\",\"0\",\"false\",\"FALSE\",\"False\" (the readBool set, utils.go:58), or \"skip-verify\"/\"preferred\" (case-insensitive). The check at utils.go:58 returns the reserved-key error.","commonSituations":"Naming a TLS config \"true\" or \"skip-verify\" while also using the built-in meaning in a DSN; refactoring that renames a config to a keyword; confusion between the boolean shorthand and a custom name.","solutions":["Choose a non-reserved, descriptive key such as \"custom\", \"client-cert\", or your app name.","Reference the same key in the DSN: `?tls=custom`.","If you only need skip-verify/preferred behavior, do not call RegisterTLSConfig at all — just use the keyword in the DSN."],"exampleFix":"// before\nmysql.RegisterTLSConfig(\"true\", cfg)\n// after\nmysql.RegisterTLSConfig(\"custom\", cfg)\n// dsn: ?tls=custom","handlingStrategy":"validation","validationCode":"reserved := map[string]bool{\"true\":true,\"false\":true,\"1\":true,\"0\":true,\"TRUE\":true,\"FALSE\":true,\"True\":true,\"False\":true,\"skip-verify\":true,\"preferred\":true}\nif reserved[strings.ToLower(key)] { return errors.New(\"tls key is reserved\") }","typeGuard":"func isReservedTLSKey(key string) bool { _, ok := readBoolExported(key); ok = ok || strings.EqualFold(key,\"skip-verify\") || strings.EqualFold(key,\"preferred\"); return ok }","tryCatchPattern":"if err := mysql.RegisterTLSConfig(key, cfg); err != nil { key = \"custom\"; mysql.RegisterTLSConfig(key, cfg) }","preventionTips":["Use descriptive, non-keyword TLS config names.","Register configs once at startup.","Reference the exact same name in the DSN."],"tags":["tls","config","validation"],"analyzedSha":"c426bd93799de0f0e094c8f0582872c529d0ed0a","analyzedAt":"2026-08-04T21:52:59.219Z","schemaVersion":2}