vitessio/vitess · error

vindex has no owner

Error message

vindex has no owner

What it means

validateExternalizedVindex requires that a vindex being externalized has an owner. An ownerless vindex cannot be safely taken over by the lookup vindex workflow, since ownership determines who maintains lookup rows during writes, so the check fails with this error.

Source

Thrown at go/vt/vtctl/workflow/lookup_vindex.go:632

		}
	}
	if targetVindex == nil {
		err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "column %s not found in target schema %s",
			sourceVindexColumn, sourceTableDefinition.Schema)
		return
	}
	return
}

// validateExternalizedVindex checks if a given vindex is externalized.
// A vindex is considered externalized if it has an owner and is not in write-only mode.
func (lv *lookupVindex) validateExternalizedVindex(vindex *vschemapb.Vindex) error {
	writeOnly, ok := vindex.Params["write_only"]
	if ok && writeOnly == "true" {
		return errors.New("vindex is in write-only mode")
	}
	if vindex.Owner == "" {
		return errors.New("vindex has no owner")
	}
	return nil
}

// validateExternalized checks if the vindexes have been externalized
// and verifies the state of the VReplication workflow on the target shards.
// It ensures that all streams in the workflow are frozen.
func (lv *lookupVindex) validateExternalized(ctx context.Context, vindexByName map[string]*vschemapb.Vindex, workflowName string, targetShards []*topo.ShardInfo) error {
	for vindexName, vindex := range vindexByName {
		if err := lv.validateExternalizedVindex(vindex); err != nil {
			return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "vindex %s has not been externalized yet: %v", vindexName, err)
		}
	}

	err := forAllShards(targetShards, func(targetShard *topo.ShardInfo) error {
		targetPrimary, err := lv.ts.GetTablet(ctx, targetShard.PrimaryAlias)
		if err != nil {
			return err

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Pass the owner table (e.g. --owner user) when creating the lookup vindex so ownership is set
  2. Set the "owner" field on the vindex in the target keyspace's vschema, or remove/recreate the vindex properly
  3. If the vindex is intentionally unowned, do not run the externalization workflow for it; use plain Materialize instead

Example fix

// before
vtctldclient CreateLookupVindex --target-keyspace lookup commerce "{'vindexes': {...}}"
// after
vtctldclient CreateLookupVindex --target-keyspace lookup --owner-table user --owner-column id commerce "{'vindexes': {...}}"
Defensive patterns

Strategy: validation

Validate before calling

v, _ := ts.GetVSchema(ctx, targetKS)
if vi := v.Vindexes[vindexName]; vi != nil && vi.Owner == "" {
    return fmt.Errorf("vindex %s has no owner; pass owner table/column or set vschema owner", vindexName)
}

Type guard

func hasOwner(v *vschemapb.Vindex) bool { return v.Owner != "" }

Try / catch

if err := lv.validateExternalized(ctx, targetShards); err != nil {
    if strings.Contains(err.Error(), "no owner") {
        return fmt.Errorf("recreate the vindex with --owner set: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Running CreateLookupVindex externalization against a vindex whose Vindex.Owner field is empty — the vindex exists in the target vschema but has no owner column vindex association and write_only is not set to true.

Common situations: Vindex was created without an --owner flag; vschema hand-edited and the owner field dropped; earlier workflow failed before assigning ownership.

Related errors


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