dgraph-io/dgraph · error

could not drop index %v from [%v] predicate when @unique dir

Error message

could not drop index %v from [%v] predicate when @unique directive specified

What it means

Companion to the @upsert rule: a predicate already marked @unique must retain a valid index tokenizer across alterations, because uniqueness is enforced through the index. validateSchemaForUnique rejects updates where prevSchema.Unique is true but validTokenizer(currentSchema.Tokenizer) is false (tokenizer removed or replaced with nothing).

Source

Thrown at worker/mutation.go:475

}

func validateSchemaForUnique(prevSchema pb.SchemaUpdate, currentSchema *pb.SchemaUpdate) error {
	validTokenizer := func(tokenizers []string) bool {
		for _, value := range tokenizers {
			if value == "hash" || value == "exact" || value == "int" {
				return true
			}
		}
		return false
	}
	if prevSchema.Predicate != "" {
		if prevSchema.Upsert && !currentSchema.Upsert {
			return errors.Errorf("could not drop @upsert from [%v] predicate when @unique directive specified",
				x.ParseAttr(currentSchema.Predicate))
		}

		if prevSchema.Unique && !validTokenizer(currentSchema.Tokenizer) {
			return errors.Errorf("could not drop index %v from [%v] predicate when @unique directive specified",
				prevSchema.Tokenizer, x.ParseAttr(currentSchema.Predicate))
		}
	}
	if !currentSchema.Upsert {
		currentSchema.Upsert = true
	}
	switch currentSchema.ValueType {
	case pb.Posting_STRING:
		if len(currentSchema.Tokenizer) == 0 ||
			(len(currentSchema.Tokenizer) > 0 && !validTokenizer(currentSchema.Tokenizer)) {

			return errors.Errorf("index for predicate [%v] is missing, add either hash or exact index with @unique",
				x.ParseAttr(currentSchema.Predicate))
		} else {
			return nil
		}

	case pb.Posting_INT:

View on GitHub (pinned to 759e242be6)

Solutions

  1. Keep a valid tokenizer: e.g. 'email: string @index(hash) @upsert @unique'.
  2. If changing tokenizers, supply a replacement valid tokenizer in the same alteration rather than an empty list.
  3. Remove @unique (and @upsert) in the same update if the index is no longer wanted.
  4. Check the accepted tokenizer list (types and tokenizer names) for the predicate's value type before proposing the change.

Example fix

// before
email: string @upsert @unique .
// after
email: string @index(hash) @upsert @unique .
Defensive patterns

Strategy: validation

Validate before calling

func validateUniqueTokenizer(prev, next *pb.SchemaUpdate) error {
    if prev != nil && prev.Predicate != "" && prev.Unique && len(next.Tokenizer) == 0 {
        return fmt.Errorf("predicate %s: @unique requires a valid index tokenizer", next.Predicate)
    }
    return nil
}

Try / catch

err := dgraph.Alter(ctx, op)
if err != nil && strings.Contains(err.Error(), "could not drop index") {
    // supply a valid tokenizer (or remove @unique) and retry
}

Prevention

When it happens

Trigger: Altering a '@unique' predicate by removing or invalidating its tokenizer, e.g. 'email: string @index(hash) @unique' changed to 'email: string @unique' or to an unsupported/empty tokenizer list.

Common situations: Switching tokenizers on a unique field without keeping a valid one; stripping @index during schema normalization; tooling that regenerates schemas and drops tokenizer arrays.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/c1b17fa681633c1d. Report an issue: GitHub.