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
- Mutate the original vector predicate (e.g. `emb` of type float32vector) instead of its `<emb|hnsw:...>` internal key.
- Filter out predicates containing the vector keyword from migration/backup replay scripts.
- 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
- Never mutate predicates returned by expand(*) that contain '|hnsw' or other internal markers.
- Filter internal predicates (hnsw, dgraph.*) out of backup-replay/migration scripts.
- Update vectors only through the original float32vector predicate.
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
- error creating indexer for %s: %w
- nil vector returned
- error fetching posting list
- UID must to be greater than 0
- UID not found/generated for xid %s
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/775a4dcfd46f9058.
Report an issue: GitHub.