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

  1. Only call ReleaseGlobalReadLock after a successful AcquireGlobalReadLock on the same Mysqld instance
  2. Track lock ownership (e.g. a bool or mutex-held flag) so cleanup code skips release when it does not hold the lock
  3. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/c1e89a7dc8d262de. Report an issue: GitHub.