gastownhall/beads · error

selecting database %q: selector returned no quoted name

Error message

selecting database %q: selector returned no quoted name

What it means

A defensive error: after the DatabaseSelector reports success, it must return the identifier-quoted database name used to schema-qualify later reads (e.g. `mydb`.dolt_ignore). An empty string means the selector violated its contract, so selectTargetDatabase cannot safely qualify subsequent unqualified reads and fails loudly instead of silently misreading another database.

Source

Thrown at internal/storage/schema/converged.go:154

	}

	var exists int
	if err := db.QueryRowContext(ctx,
		"SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = ?",
		databaseName,
	).Scan(&exists); err != nil {
		return false, "", fmt.Errorf("probing database %q existence: %w", databaseName, err)
	}
	if exists == 0 {
		return false, "", nil
	}

	quoted, err := selector(ctx, db, databaseName)
	if err != nil {
		return false, "", fmt.Errorf("selecting database %q: %w", databaseName, err)
	}
	if quoted == "" {
		return false, "", fmt.Errorf("selecting database %q: selector returned no quoted name", databaseName)
	}
	return true, quoted, nil
}

// migrationLockFree reports whether the database-scoped migration lock is
// currently unheld. IS_FREE_LOCK is a read: it never queues, never acquires,
// and costs one round trip, which is the entire point — the fast path exists
// to stop paying GET_LOCK's queue.
//
// A NULL answer means the server would not tell us, which is not the same as
// "free": fail closed.
func migrationLockFree(ctx context.Context, db DBConn, lockName string) (bool, error) {
	var free sql.NullInt64
	if err := db.QueryRowContext(ctx, "SELECT IS_FREE_LOCK(?)", lockName).Scan(&free); err != nil {
		return false, fmt.Errorf("probing migration lock %q: %w", lockName, err)
	}
	if !free.Valid {
		return false, nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Fix the DatabaseSelector implementation to always return the backtick-quoted database name on success (e.g. fmt.Sprintf("`%s`", name) after validating the name)
  2. Check the selector's empty-return code path (early returns, helper functions returning "")
  3. If using the library-provided selectors, report/upgrade — stock selectors always quote

Example fix

// before
func mySelector(ctx context.Context, db DBConn, name string) (string, error) {
    if _, err := db.ExecContext(ctx, "USE "+name); err != nil { return "", err }
    return "", nil // BUG: forgot to return quoted name
}
// after
func mySelector(ctx context.Context, db DBConn, name string) (string, error) {
    quoted := "`" + name + "`"
    if _, err := db.ExecContext(ctx, "USE "+quoted); err != nil { return "", err }
    return quoted, nil
}
Defensive patterns

Strategy: validation

Validate before calling

// validate a custom selector's contract before installing it
quoted, err := sel(ctx, db, "testdb")
if err != nil || quoted == "" {
    return fmt.Errorf("selector must return non-empty quoted name")
}

Type guard

func validSelector(sel DatabaseSelector) bool {
    return sel != nil
}
// and assert its output: quoted != "" before proceeding

Prevention

When it happens

Trigger: A custom DatabaseSelector implementation returns ("", nil) — i.e. it claims success but does not return the quoted identifier. Only possible when the caller supplies a non-nil selector and the session was not already on the target database.

Common situations: A selector implementation that issues USE but forgets to build/return the quoted name; a selector returning the result of a helper that itself returns empty on some code path; writing a new selector for an embedded driver and mishandling the success branch.

Related errors


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