vitessio/vitess · error

a conflicting vindex named %v already exists in the target v

Error message

a conflicting vindex named %v already exists in the target vschema

What it means

When the target keyspace is sharded, Vitess chooses a primary vindex type (hash/binary/unicode hash etc.) for the lookup table and checks whether that type-name already exists in the target vschema's Vindexes map. If an entry with that name exists but with a different definition (proto.Equal fails), creation aborts to avoid overwriting an unrelated vindex.

Source

Thrown at go/vt/wrangler/materializer.go:756

		for _, field := range tableSchema.TableDefinitions[0].Fields {
			if sourceVindexColumns[0] == field.Name {
				targetVindexType, err = vindexes.ChooseVindexForType(field.Type)
				if err != nil {
					return nil, nil, nil, err
				}
				targetVindex = &vschemapb.Vindex{
					Type: targetVindexType,
				}
				break
			}
		}
		if targetVindex == nil {
			// Unreachable. We validated column names when generating the DDL.
			return nil, nil, nil, fmt.Errorf("column %s not found in schema %v", sourceVindexColumns[0], tableSchema.TableDefinitions[0])
		}
		if existing, ok := targetVSchema.Vindexes[targetVindexType]; ok {
			if !proto.Equal(existing, targetVindex) {
				return nil, nil, nil, fmt.Errorf("a conflicting vindex named %v already exists in the target vschema", targetVindexType)
			}
		} else {
			targetVSchema.Vindexes[targetVindexType] = targetVindex
		}

		targetTable = &vschemapb.Table{
			ColumnVindexes: []*vschemapb.ColumnVindex{{
				Column: vindexFromCols[0],
				Name:   targetVindexType,
			}},
		}
	} else {
		targetTable = &vschemapb.Table{}
	}
	if existing, ok := targetVSchema.Tables[targetTableName]; ok {
		if !proto.Equal(existing, targetTable) {
			return nil, nil, nil, fmt.Errorf("a conflicting table named %v already exists in the target vschema", targetTableName)
		}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Rename your custom vindex in the target vschema to something other than the built-in type name, or accept the built-in plain definition and update the vschema
  2. Inspect the target vschema (`vtctldclient GetVSchema`) to see the conflicting entry and reconcile it
  3. If the existing definition is obsolete, remove it and retry

Example fix

// before: custom vindex shadowing the built-in name
"vindexes": {"hash": {"type": "hash", "params": {"hash": "xxhash"}}}
// after: use a distinct name
"vindexes": {"xxhash_vdx": {"type": "hash", "params": {"hash": "xxhash"}}}
Defensive patterns

Strategy: validation

Validate before calling

tvs, _ := ts.GetVSchema(ctx, targetKeyspace)
for name, v := range tvs.Vindexes {
    if name == "hash" || name == "binary" || strings.HasPrefix(name, "unicode_lo") {
        if len(v.Params) > 0 || v.Owner != "" {
            return fmt.Errorf("built-in vindex name %s is shadowed with custom params", name)
        }
    }
}

Type guard

func shadowsBuiltInVindex(tvs *vschemapb.Keyspace, typeName string, want *vschemapb.Vindex) bool {
    existing, ok := tvs.Vindexes[typeName]
    return ok && !proto.Equal(existing, want)
}

Try / catch

if err := createLookupVindex(...); err != nil {
    if strings.Contains(err.Error(), "conflicting vindex named") {
        return renameCustomVindex(targetKS, typeName) // free the built-in name, retry
    }
    return err
}

Prevention

When it happens

Trigger: targetVSchema.Vindexes already contains an entry keyed by the chosen type string (e.g. "hash") with different settings than the plain vindex Vitess wants to add — e.g. the operator redefined `hash` with custom params.

Common situations: Target vschema has a custom-parameterized vindex named `hash` (or `binary`/`unicode_lo_xx`) from earlier manual config; applying legacy vschema files that name built-in types with extra params.

Related errors


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