vitessio/vitess · error

failed to lock %s; if you really want to delete this keyspac

Error message

failed to lock %s; if you really want to delete this keyspace, re-run with Force=true: %w

What it means

DeleteKeyspace acquires a keyspace lock to serialize against other workflows. If locking fails and Force is false, the delete is refused with this message telling the operator to re-run with Force=true. With Force=true, the lock failure is only logged and deletion proceeds unsafely.

Source

Thrown at go/vt/vtctl/grpcvtctldserver/server.go:1146

}

// DeleteKeyspace is part of the vtctlservicepb.VtctldServer interface.
func (s *VtctldServer) DeleteKeyspace(ctx context.Context, req *vtctldatapb.DeleteKeyspaceRequest) (resp *vtctldatapb.DeleteKeyspaceResponse, err error) {
	span, ctx := trace.NewSpan(ctx, "VtctldServer.DeleteKeyspace")
	defer span.Finish()

	defer panicHandler(&err)

	span.Annotate("keyspace", req.Keyspace)
	span.Annotate("recursive", req.Recursive)
	span.Annotate("force", req.Force)

	lctx, unlock, lerr := s.ts.LockKeyspace(ctx, req.Keyspace, "DeleteKeyspace")
	switch {
	case lerr == nil:
		ctx = lctx
	case !req.Force:
		err = fmt.Errorf("failed to lock %s; if you really want to delete this keyspace, re-run with Force=true: %w", req.Keyspace, lerr)
		return nil, err
	default:
		log.Warn(fmt.Sprintf("%s: failed to lock keyspace %s for deletion, but force=true, proceeding anyway ...", lerr, req.Keyspace))
	}

	if unlock != nil {
		defer func() {
			// Attempting to unlock a keyspace we successfully deleted results
			// in ts.unlockKeyspace returning an error, which can make the
			// overall RPC _seem_ like it failed.
			//
			// So, we do this extra checking to allow for specifically this
			// scenario to result in "success."
			origErr := err
			unlock(&err)
			if origErr == nil && topo.IsErrType(err, topo.NoNode) {
				err = nil
			}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Wait for the holding workflow to finish, then retry DeleteKeyspace without Force
  2. Find and clean up stale locks (check topo lock paths / workflow status) if no workflow is actually running
  3. Re-run with Force=true only when certain no workflow is active, accepting the risk of concurrent modification
Defensive patterns

Strategy: validation

Validate before calling

ks, err := client.GetKeyspace(ctx, name)
if err != nil {
    return fmt.Errorf("keyspace state unknown: %w", err)
}
// confirm no active workflows (Reshard/MoveTables) before deleting

Try / catch

_, err := client.DeleteKeyspace(ctx, req)
if err != nil && strings.Contains(err.Error(), "failed to lock") {
    // find lock holder or wait; only use Force after verifying no workflow
}

Prevention

When it happens

Trigger: Calling DeleteKeyspace while another workflow holds the keyspace lock (e.g. a running Reshard, MoveTables, or another vtctld operation), or the lock cannot be taken due to topo errors, when Force is not set.

Common situations: Deleting a keyspace while a migration is in flight; a stale lock from a crashed workflow; concurrent operator actions in a shared cluster.

Related errors


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