gastownhall/beads · error
probing migration lock %q: %w
Error message
probing migration lock %q: %w
What it means
migrationLockFree probes the database-scoped migration lock with SELECT IS_FREE_LOCK(?) — a read that never queues or acquires. This error wraps a failure of that IS_FREE_LOCK query itself. A NULL result is handled separately (fail closed to the locked path, no error); only an actual query error produces this message.
Source
Thrown at internal/storage/schema/converged.go:169
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
}
return free.Int64 == 1, nil
}
// doltIgnoreSeeded reports whether every canonical dolt_ignore pattern
// seedDoltIgnorePatterns would assert is already present. It is the read-only
// counterpart of that seed and shares its version gate: a pattern whose flip
// migration has not been reached yet is not expected, exactly as the seed
// would not insert it. mainVersionAtLeast is a lower bound on the main cursor
// that the caller has already established, so this read costs one round trip
// rather than three.
//
// Presence is judged on the pattern alone, never on its ignored value, because
// INSERT IGNORE would leave an explicit operator override (a pattern recorded
// with ignored=false) untouched. Reporting such a row as missing would sendView on GitHub (pinned to 71377f2769)
Solutions
- Retry — the probe is read-only and alreadyConverged fails closed (falls back to the normal locked path), so the error may be transient
- Inspect the wrapped %w driver error; if connection-level, reconnect and re-run bd
- If IS_FREE_LOCK is unsupported by your Dolt server version, upgrade the server or skip the lock-free fast path
- Check context timeouts if deadlines recur on a busy shared server
Defensive patterns
Strategy: fallback
Validate before calling
// verify the server supports named locks before fast-path probing
var v string
_ = db.QueryRow("SELECT VERSION()").Scan(&v) // check Dolt version supports IS_FREE_LOCK Try / catch
if _, err := alreadyConverged(...); err != nil {
// alreadyConverged errors are advisory: proceed down the locked MigrateUp path
return MigrateUp(ctx, db)
} Prevention
- Treat alreadyConverged failures as 'not converged', never fatal
- Ensure the Dolt server version supports IS_FREE_LOCK
- Retry transient connection errors rather than surfacing them to users
When it happens
Trigger: The SELECT IS_FREE_LOCK(?) query fails: broken pooled connection, context cancelled/deadline exceeded, server rejected the statement, or the driver errored mid-scan.
Common situations: Dolt sql-server connection dropped between earlier probes and this one; context timeout during a slow open on a loaded shared rig; a Dolt build/server version that rejects or errors on IS_FREE_LOCK.
Related errors
- %w (lock release also failed: %w)
- schema: acquire migration lock: %w: %w
- schema: acquire migration lock: %w: returned NULL
- dolt directory is required
- dolt binary not found
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/88fe9579d3140449.
Report an issue: GitHub.