gastownhall/beads · error

ensure global db: failed to open connection: %w

Error message

ensure global db: failed to open connection: %w

What it means

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.

Source

Thrown at internal/doltserver/doltserver.go:1537

// beads_global database if it doesn't already exist. This is idempotent and
// safe to call on every shared server init. Schema initialization and config
// seeding (issue prefix, project ID) are handled by the store layer when the
// global database is first opened with CreateIfMissing=true.
//
// Returns nil if the database already exists or was successfully created.
func EnsureGlobalDatabase(host string, port int, user, password string) error {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	dsn := doltutil.ServerDSN{
		Host:     host,
		Port:     port,
		User:     user,
		Password: password,
	}.String()
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return fmt.Errorf("ensure global db: failed to open connection: %w", err)
	}
	defer db.Close()
	db.SetMaxOpenConns(1)
	db.SetConnMaxLifetime(10 * time.Second)

	if err := db.PingContext(ctx); err != nil {
		return fmt.Errorf("ensure global db: server not reachable: %w", err)
	}

	// CREATE DATABASE IF NOT EXISTS is idempotent — safe on every call.
	// GlobalDatabaseName is a constant ("beads_global"), not user input.
	_, err = db.ExecContext(ctx, fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`", GlobalDatabaseName)) //nolint:gosec // G201: constant database name
	if err != nil {
		errLower := strings.ToLower(err.Error())
		if !strings.Contains(errLower, "database exists") && !strings.Contains(errLower, "1007") {
			return fmt.Errorf("ensure global db: failed to create %s: %w", GlobalDatabaseName, err)
		}
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped error text for 'invalid DSN' and URL-escape credentials containing @ : / ( ) characters
  2. Verify host/port/user/password values in bd config are clean (no stray quotes or whitespace)
  3. Re-check credentials: temporary password with special chars from an automated setup is the usual culprit
  4. If the driver is missing in a forked build, add _ "github.com/go-sql-driver/mysql" to imports
  5. If Open still fails with valid DSN, retry once — transient driver init failures are rare; persistent ones indicate a broken binary

Example fix

// before: raw password breaks the DSN
// password := "p@ss:word"  // DSN parse error
// after: escape credentials before building the DSN
dsnUser := url.QueryEscape(user)
dsnPassword := url.QueryEscape(password)
dsn := doltutil.ServerDSN{Host: host, Port: port, User: dsnUser, Password: dsnPassword}.String()
Defensive patterns

Strategy: validation

Validate before calling

// Validate credentials can form a clean DSN before calling EnsureGlobalDatabase
func validDSNPart(s string) bool {
    if s == "" { return true }
    for _, r := range s {
        if strings.ContainsRune("@:/(", r) { return false }
    }
    return true
}
if !validDSNPart(user) || !validDSNPart(password) {
    return errors.New("db user/password contain DSN metacharacters; escape or change them")
}

Try / catch

if err := doltserver.EnsureGlobalDatabase(host, port, user, password); err != nil {
    var dsnErr *error
    if strings.Contains(err.Error(), "failed to open connection") {
        return fmt.Errorf("bad DSN (check special chars in user/password): %w", err)
    }
    _ = dsnErr
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/fe040344f3ab95d8. Report an issue: GitHub.