golang-migrate/migrate · error

could not parse x-statement-timeout as float: %w

Error message

could not parse x-statement-timeout as float: %w

What it means

The MySQL driver accepts an optional x-statement-timeout custom parameter, parsed as an integer with strconv.Atoi. If the value is present but not a valid integer, Open returns this error wrapping the parse failure. Note the message says 'as float' but the code actually requires an integer — fractional values like '1.5' will fail.

Source

Thrown at database/mysql/mysql.go:250

	customParams, err := extractCustomQueryParams(config)
	if err != nil {
		return nil, err
	}

	noLockParam, noLock := customParams["x-no-lock"], false
	if noLockParam != "" {
		noLock, err = strconv.ParseBool(noLockParam)
		if err != nil {
			return nil, fmt.Errorf("could not parse x-no-lock as bool: %w", err)
		}
	}

	statementTimeoutParam := customParams["x-statement-timeout"]
	statementTimeout := 0
	if statementTimeoutParam != "" {
		statementTimeout, err = strconv.Atoi(statementTimeoutParam)
		if err != nil {
			return nil, fmt.Errorf("could not parse x-statement-timeout as float: %w", err)
		}
	}

	db, err := sql.Open("mysql", config.FormatDSN())
	if err != nil {
		return nil, err
	}

	mx, err := WithInstance(db, &Config{
		DatabaseName:     config.DBName,
		MigrationsTable:  customParams["x-migrations-table"],
		NoLock:           noLock,
		StatementTimeout: time.Duration(statementTimeout) * time.Millisecond,
	})
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Set x-statement-timeout to a plain integer (seconds), e.g. x-statement-timeout=5
  2. Remove the parameter if no statement timeout is wanted (it defaults to 0, i.e. disabled)
  3. Strip units/fractional parts in code before composing the URL

Example fix

// before
url := "mysql://user:pass@tcp(db:3306)/app?x-statement-timeout=500ms"
// after
url := "mysql://user:pass@tcp(db:3306)/app?x-statement-timeout=5"
Defensive patterns

Strategy: validation

Validate before calling

if v := params.Get("x-statement-timeout"); v != "" {
    if _, err := strconv.Atoi(v); err != nil {
        return fmt.Errorf("x-statement-timeout must be an integer (seconds), got %q", v)
    }
}

Try / catch

drv, err := mysql.Open(dsn)
if err != nil && strings.Contains(err.Error(), "could not parse x-statement-timeout") {
    return fmt.Errorf("x-statement-timeout must be a plain integer, no units or decimals: %w", err)
}

Prevention

When it happens

Trigger: Calling Open with a mysql:// URL containing x-statement-timeout set to a non-integer string, e.g. x-statement-timeout=500ms, x-statement-timeout=1.5, or an empty-but-present templated value (database/mysql/mysql.go:248-253).

Common situations: Developers copy duration syntax from other drivers (e.g. Postgres statement_timeout in ms with units); templating substitutes a decimal default; confusion with the message text leads to trying floats, which also fail because Atoi is used.

Understand the failure class

Related errors


AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02). Data as JSON: /api/errors/a11eb6c3634f8b59. Report an issue: GitHub.