{"record":{"id":"fe040344f3ab95d8","repo":"gastownhall/beads","slug":"ensure-global-db-failed-to-open-connection-w","errorCode":null,"errorMessage":"ensure global db: failed to open connection: %w","messagePattern":"ensure global db: failed to open connection: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/doltserver/doltserver.go","lineNumber":1537,"sourceCode":"// beads_global database if it doesn't already exist. This is idempotent and\n// safe to call on every shared server init. Schema initialization and config\n// seeding (issue prefix, project ID) are handled by the store layer when the\n// global database is first opened with CreateIfMissing=true.\n//\n// Returns nil if the database already exists or was successfully created.\nfunc EnsureGlobalDatabase(host string, port int, user, password string) error {\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tdsn := doltutil.ServerDSN{\n\t\tHost:     host,\n\t\tPort:     port,\n\t\tUser:     user,\n\t\tPassword: password,\n\t}.String()\n\tdb, err := sql.Open(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"ensure global db: failed to open connection: %w\", err)\n\t}\n\tdefer db.Close()\n\tdb.SetMaxOpenConns(1)\n\tdb.SetConnMaxLifetime(10 * time.Second)\n\n\tif err := db.PingContext(ctx); err != nil {\n\t\treturn fmt.Errorf(\"ensure global db: server not reachable: %w\", err)\n\t}\n\n\t// CREATE DATABASE IF NOT EXISTS is idempotent — safe on every call.\n\t// GlobalDatabaseName is a constant (\"beads_global\"), not user input.\n\t_, err = db.ExecContext(ctx, fmt.Sprintf(\"CREATE DATABASE IF NOT EXISTS `%s`\", GlobalDatabaseName)) //nolint:gosec // G201: constant database name\n\tif err != nil {\n\t\terrLower := strings.ToLower(err.Error())\n\t\tif !strings.Contains(errLower, \"database exists\") && !strings.Contains(errLower, \"1007\") {\n\t\t\treturn fmt.Errorf(\"ensure global db: failed to create %s: %w\", GlobalDatabaseName, err)\n\t\t}\n\t}","sourceCodeStart":1519,"sourceCodeEnd":1555,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/doltserver/doltserver.go#L1519-L1555","documentation":"EnsureGlobalDatabase wraps any error from sql.Open(\"mysql\", dsn) with this message. sql.Open only validates the DSN and loads the driver — it does not touch the network — so this almost always means the DSN string is malformed (bad characters in user/password/host) or the mysql driver failed to register/initialize. It does NOT mean the server is down; that surfaces later as the 'server not reachable' ping error.","triggerScenarios":"sql.Open(\"mysql\", dsn) returns an error when EnsureGlobalDatabase builds a ServerDSN from host, port, user, password — typically an invalid DSN (unescaped special characters like @, :, /, ( in user or password), or the go-sql-driver/mysql driver not imported/registered.","commonSituations":"Password or username containing DSN metacharacters (@ : / ( ) set in BEADS_DB_PASSWORD or config) without escaping; config file with stray whitespace/quotes in credentials; a build missing the mysql driver blank import.","solutions":["Check the wrapped error text for 'invalid DSN' and URL-escape credentials containing @ : / ( ) characters","Verify host/port/user/password values in bd config are clean (no stray quotes or whitespace)","Re-check credentials: temporary password with special chars from an automated setup is the usual culprit","If the driver is missing in a forked build, add _ \"github.com/go-sql-driver/mysql\" to imports","If Open still fails with valid DSN, retry once — transient driver init failures are rare; persistent ones indicate a broken binary"],"exampleFix":"// before: raw password breaks the DSN\n// password := \"p@ss:word\"  // DSN parse error\n// after: escape credentials before building the DSN\ndsnUser := url.QueryEscape(user)\ndsnPassword := url.QueryEscape(password)\ndsn := doltutil.ServerDSN{Host: host, Port: port, User: dsnUser, Password: dsnPassword}.String()","handlingStrategy":"validation","validationCode":"// Validate credentials can form a clean DSN before calling EnsureGlobalDatabase\nfunc validDSNPart(s string) bool {\n    if s == \"\" { return true }\n    for _, r := range s {\n        if strings.ContainsRune(\"@:/(\", r) { return false }\n    }\n    return true\n}\nif !validDSNPart(user) || !validDSNPart(password) {\n    return errors.New(\"db user/password contain DSN metacharacters; escape or change them\")\n}","typeGuard":null,"tryCatchPattern":"if err := doltserver.EnsureGlobalDatabase(host, port, user, password); err != nil {\n    var dsnErr *error\n    if strings.Contains(err.Error(), \"failed to open connection\") {\n        return fmt.Errorf(\"bad DSN (check special chars in user/password): %w\", err)\n    }\n    _ = dsnErr\n    return err\n}","preventionTips":["Avoid @ : / ( ) in database credentials; URL-escape if unavoidable","Keep credentials in env vars/config, never inline in code","Ensure go-sql-driver/mysql is imported in any custom build","Sanitize config files for stray quotes/whitespace in credential fields"],"tags":["go","mysql","dsn","connection"],"backgroundTag":"invalid-dsn","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}