dgraph-io/dgraph · error
could not insert duplicate value [%v] for predicate [%v]
Error message
could not insert duplicate value [%v] for predicate [%v]
What it means
Dgraph enforces @unique on a predicate during upsert mutations. When the mutation's value comes from a val(var) variable, verifyUnique iterates all existing UIDs holding the value and rejects the mutation if another node (different UID from the mutation subject) already stores the same value. This is the normal unique-constraint violation for value-variable mutations — Dgraph refuses to create a second node with the same value for a @unique predicate.
Source
Thrown at edgraph/server.go:1824
return errors.Wrapf(err, "error while parsing [%v]", pred.Subject)
}
}
var predValue interface{}
if strings.HasPrefix(pred.ObjectId, "val(") {
varName := qr.Vars[pred.ObjectId[4:len(pred.ObjectId)-1]]
val, ok := varName.Vals.Get(0)
if !ok {
_, isValueGoingtoSet := varName.Vals.Get(subjectUid)
if !isValueGoingtoSet {
continue
}
results := qr.Vars[queryVar.valVar]
err := results.Vals.Iterate(func(uidOfv uint64, v types.Val) error {
varNameVal, _ := varName.Vals.Get(subjectUid)
if v.Value == varNameVal.Value && uidOfv != subjectUid {
return errors.Errorf("could not insert duplicate value [%v] for predicate [%v]",
v.Value, pred.Predicate)
}
return nil
})
if err != nil {
return err
}
continue
} else {
predValue = val.Value
}
} else {
predValue = dql.TypeValFrom(pred.ObjectValue).Value
}
// Here, we check the uniqueness of the triple by comparing the result of the uniqueQuery with the triple.
if !isEmpty(queryResult.Uids) {
if len(queryResult.Uids.Uids) > 1 {View on GitHub (pinned to 759e242be6)
Solutions
- Catch the error and treat it as a business-rule violation: run a query for the existing value and return that node instead of inserting
- Use an upsert block so the same value updates the existing node (blank node mapped to uid(var)) rather than inserting a duplicate
- Ensure the predicate has an index (e.g. @index(exact) for strings) — the unique check requires it and it makes dedup queries cheap
- If the duplicate is unintended legacy data, delete or merge the colliding nodes before re-running the mutation
Example fix
// before
upsert(query: "{ v as var(func: eq(email, \"a@b.com\")) }",
mutation: "{ set { _:u <email> val(v) . } }") // rejects if email exists elsewhere
// after
upsert(query: "{ v as var(func: eq(email, \"a@b.com\")) }",
mutation: "{ set { uid(v) <email> \"a@b.com\" . uid(v) <name> \"A\" . } }") Defensive patterns
Strategy: try-catch
Validate before calling
// Before insert, check existence:
q := `{ q(func: eq(email, $val)) { uid } }`
resp, _ := dg.NewTxn().Query(ctx, q)
// if resp has nodes, update instead of insert Try / catch
_, err := txn.Mutate(ctx, mu)
if err != nil && strings.Contains(err.Error(), "could not insert duplicate value") {
// fetch existing node by value and return/merge it
return getExistingByValue(ctx, value)
} Prevention
- Declare @unique with a supporting index (e.g. @index(exact)) in the schema
- Prefer upsert blocks over blind inserts for unique fields
- Map blank nodes to uid(var) so existing nodes are updated, not duplicated
- Make signup flows idempotent: query-then-update keyed on the unique value
When it happens
Trigger: An upsert mutation sets predicate P (declared @unique in schema) with object val(v) where v holds a value, and the uniquecheck query finds an existing node with that same value whose UID differs from the subject UID (edgraph/server.go:1824).
Common situations: Creating a second user with the same email/username in a schema like `email: string @unique @index(exact)`; concurrent signups racing for the same email; re-running a seed script that inserts records already present; syncing data from an external system that already has rows with colliding values.
Related errors
- error while writing string
- error while building unique query
- UID must to be greater than 0
- UID not found/generated for xid %s
- Subject and object both should be *. Got: %+v
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/b3a6b003bda0ce48.
Report an issue: GitHub.