vitessio/vitess · error
no read locks acquired yet
Error message
no read locks acquired yet
What it means
ReleaseGlobalReadLock requires that a global read lock was previously acquired: mysqld.lockConn must be non-nil. Releasing without a prior acquisition returns this error, since there is no lock connection on which to issue UNLOCK TABLES. It protects the pairing of Acquire/Release calls on the same Mysqld instance.
Source
Thrown at go/vt/mysqlctl/query.go:343
conn, err := getPoolReconnect(ctx, mysqld.dbaPool)
if err != nil {
return err
}
err = mysqld.executeSuperQueryListConn(ctx, conn, []string{"FLUSH TABLES WITH READ LOCK"})
if err != nil {
conn.Recycle()
return err
}
mysqld.lockConn = conn
return nil
}
func (mysqld *Mysqld) ReleaseGlobalReadLock(ctx context.Context) error {
if mysqld.lockConn == nil {
return errors.New("no read locks acquired yet")
}
err := mysqld.executeSuperQueryListConn(ctx, mysqld.lockConn, []string{"UNLOCK TABLES"})
if err != nil {
return err
}
mysqld.lockConn.Recycle()
mysqld.lockConn = nil
return nil
}
const (
sourcePasswordStart = " SOURCE_PASSWORD = '"
sourcePasswordEnd = "',\n"
masterPasswordStart = " MASTER_PASSWORD = '"
masterPasswordEnd = "',\n"
identifiedByStart = " IDENTIFIED BY '"View on GitHub (pinned to 01a25a7d17)
Solutions
- Only call ReleaseGlobalReadLock after a successful AcquireGlobalReadLock on the same Mysqld instance
- Track lock ownership (e.g. a bool or mutex-held flag) so cleanup code skips release when it does not hold the lock
- Make acquire/release pairs symmetric — acquire first, defer release, so the release only runs on success
Example fix
// before
err := mysqld.AcquireGlobalReadLock(ctx)
if err != nil { return err }
// ... later, double release ...
mysqld.ReleaseGlobalReadLock(ctx)
mysqld.ReleaseGlobalReadLock(ctx) // error: no read locks acquired yet
// after
if err := mysqld.AcquireGlobalReadLock(ctx); err == nil {
defer mysqld.ReleaseGlobalReadLock(ctx)
} Defensive patterns
Strategy: validation
Validate before calling
var lockHeld bool
if !lockHeld {
return nil // nothing to release
}
err := mysqld.ReleaseGlobalReadLock(ctx) Try / catch
if err := mysqld.ReleaseGlobalReadLock(ctx); err != nil {
if strings.Contains(err.Error(), "no read locks acquired yet") {
return nil // nothing held; safe to ignore
}
return err
} Prevention
- Only release after a confirmed successful acquire
- Use defer-based release immediately after a successful acquire
- Keep acquire/release within one component so ownership is unambiguous
When it happens
Trigger: Calling ReleaseGlobalReadLock without a prior successful AcquireGlobalReadLock; calling Release twice (second call finds lockConn already nil); releasing on a different Mysqld instance than the one that acquired.
Common situations: Cleanup/defer paths that run even when acquisition failed or never happened; error paths that release unconditionally; splitting acquire and release across components that each construct their own Mysqld.
Related errors
- lock already acquired
- ReadFile cannot be called on read-write backup
- AddFile cannot be called on read-only backup
- EndBackup cannot be called on read-only backup
- AbortBackup cannot be called on read-only backup
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/c1e89a7dc8d262de.
Report an issue: GitHub.