golang-migrate/migrate · error

x-no-tx-wrap: %s

Error message

x-no-tx-wrap: %s

What it means

The sqlcipher driver's Open parses the x-no-tx-wrap URL query parameter as a boolean (strconv.ParseBool) to decide whether each migration should run outside a transaction. If the value is present but not a valid boolean ("1", "t", "true", "0", "f", "false"), Open returns fmt.Errorf("x-no-tx-wrap: %s", err). The error message embeds the underlying parse error and the offending value.

Source

Thrown at database/sqlcipher/sqlcipher.go:112

	}
	dbfile := strings.Replace(migrate.FilterCustomQuery(purl).String(), "sqlite3://", "", 1)
	db, err := sql.Open("sqlite3", dbfile)
	if err != nil {
		return nil, err
	}

	qv := purl.Query()

	migrationsTable := qv.Get("x-migrations-table")
	if len(migrationsTable) == 0 {
		migrationsTable = DefaultMigrationsTable
	}

	noTxWrap := false
	if v := qv.Get("x-no-tx-wrap"); v != "" {
		noTxWrap, err = strconv.ParseBool(v)
		if err != nil {
			return nil, fmt.Errorf("x-no-tx-wrap: %s", err)
		}
	}

	mx, err := WithInstance(db, &Config{
		DatabaseName:    purl.Path,
		MigrationsTable: migrationsTable,
		NoTxWrap:        noTxWrap,
	})
	if err != nil {
		return nil, err
	}
	return mx, nil
}

func (m *Sqlite) Close() error {
	return m.db.Close()
}

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Change the query parameter value to a strconv.ParseBool-accepted string: 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
  2. Remove the x-no-tx-wrap parameter entirely if you want the default (transaction-wrapped) behavior.
  3. Trim whitespace and shell quotes from the connection string before passing it to migrate.Open/sql.Open.

Example fix

// before
dsn := "sqlcipher://data/app.db?x-no-tx-wrap=yes" // parse error
// after
dsn := "sqlcipher://data/app.db?x-no-tx-wrap=true"
Defensive patterns

Strategy: validation

Validate before calling

if v := q.Get("x-no-tx-wrap"); v != "" {
    if _, err := strconv.ParseBool(v); err != nil {
        return fmt.Errorf("invalid x-no-tx-wrap %q: use 1/t/true or 0/f/false", v)
    }
}

Type guard

func validBoolParam(v string) bool {
    _, err := strconv.ParseBool(v)
    return err == nil
}

Try / catch

d, err := sqlcipher.Open(dsn)
if err != nil {
    if strings.HasPrefix(err.Error(), "x-no-tx-wrap:") {
        return fmt.Errorf("fix DSN boolean param (allowed: 1,t,true,0,f,false): %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Opening with a URL like sqlcipher://db.sqlite?x-no-tx-wrap=yes (or "on", "TRUE ", any value strconv.ParseBool rejects) — the parameter is present and non-empty but not a Go-recognized bool string.

Common situations: Users writing human-style booleans ("yes", "on", "y") in the DSN; copy-pasting flags from other tools that accept different boolean syntax; trailing whitespace or shell quoting artifacts in the connection string from env vars.

Related errors


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