dgraph-io/dgraph · error

Not allowed to insert mutations in vector index keys, edge:

Error message

Not allowed to insert mutations in vector index keys, edge: [%v]

What it means

Dgraph stores HNSW vector index internals under predicates whose name contains the vector keyword (e.g. a predicate ending in '|hnsw:vector'). These internal edges are managed by the vector index itself; ValidateAndConvert rejects any user mutation whose predicate name contains that keyword so clients cannot corrupt the index structure.

Source

Thrown at worker/mutation.go:517

		}
	}

	return errors.Errorf("@unique directive not supported on [%v] type predicate", currentSchema.ValueType.String())
}

// ValidateAndConvert checks compatibility or converts to the schema type if the storage type is
// specified. If no storage type is specified then it converts to the schema type.
func ValidateAndConvert(edge *pb.DirectedEdge, su *pb.SchemaUpdate) error {

	if isDeletePredicateEdge(edge) {
		return nil
	}
	if types.TypeID(edge.ValueType) == types.DefaultID && isStarAll(edge.Value) {
		return nil
	}

	if strings.Contains(su.Predicate, hnsw.VecKeyword) {
		return errors.Errorf("Not allowed to insert mutations in vector index keys, edge: [%v]", edge)
	}

	storageType := posting.TypeID(edge)
	schemaType := types.TypeID(su.ValueType)

	// type checks
	switch {
	case edge.Lang != "" && !su.GetLang():
		return errors.Errorf("Attr: [%v] should have @lang directive in schema to mutate edge: [%v]",
			x.ParseAttr(edge.Attr), edge)

	case !schemaType.IsScalar() && !storageType.IsScalar():
		return nil

	case !schemaType.IsScalar() && storageType.IsScalar():
		return errors.Errorf("Input for predicate %q of type uid is scalar. Edge: %v",
			x.ParseAttr(edge.Attr), edge)

View on GitHub (pinned to 759e242be6)

Solutions

  1. Mutate the original vector predicate (e.g. `emb` of type float32vector) instead of its `<emb|hnsw:...>` internal key.
  2. Filter out predicates containing the vector keyword from migration/backup replay scripts.
  3. Use the standard mutation APIs to update vectors; the HNSW index is maintained automatically.

Example fix

// before (fails)
<0x1> <emb|hnsw:vector> "[0.1,0.2]" .

// after
<0x1> <emb> "[0.1,0.2]"^^<xs:floatVector> .
Defensive patterns

Strategy: validation

Validate before calling

const vecKeyword = "|hnsw"
function isInternalVectorPred(pred) {
  return typeof pred === 'string' && pred.includes(vecKeyword)
}
// filter all N-Quads before mutation:
nquads = nquads.filter(nq => !isInternalVectorPred(nq.predicate))

Type guard

function isUserPredicate(pred) {
  return typeof pred === 'string' && !pred.includes('|hnsw') && !pred.includes('dgraph.')
}

Try / catch

try {
  await dgraph.mutation(setNquads)
} catch (e) {
  if (String(e).includes('vector index keys')) {
    // strip internal hnsw predicates from the batch and re-run
  }
}

Prevention

When it happens

Trigger: Running a mutation (via runMutation -> proposeAndWait) that writes to a predicate whose name contains 'hnsw' (VecKeyword), typically by copy-pasting the internal vector-index predicate name from a query or backup instead of the original vector predicate.

Common situations: Hand-crafting RDF/N-Quad mutations against internal index keys seen in * (expand-all) results; backup/restore tooling or scripts that replay every predicate including internal vector index keys; typos appending the hnsw suffix to a vector predicate.

Related errors


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