dgraph-io/dgraph · critical

Found multiple nodes with ID: %s

Error message

Found multiple nodes with ID: %s

What it means

During a mutation, an upsert/query block produced more than one UID for a single query name (qNameToUID counted >1 results). Since the node should be uniquely identified by its XID or UID, multiple matches indicate data corruption — duplicate nodes sharing the same external ID — so the mutation is aborted rather than silently modifying the wrong node.

Source

Thrown at graphql/resolve/mutation.go:356

	// 		"Project_1" : "0x123",
	// 		"Column_2" : "0x234"
	// }
	// As only Add and Update mutations generate queries using RewriteQueries,
	// qNameToUID map will be non-empty only in case of Add or Update Mutation.
	qNameToUID := make(map[string]string)
	for key, result := range queryResultMap {
		count := 0
		typ := qNameToType[key]
		for _, res := range result {
			if x.HasString(res.Types, typ) {
				qNameToUID[key] = res.Uid
				count++
			}
		}
		if count > 1 {
			// Found multiple UIDs for query. This should ideally not happen.
			// This indicates that there are multiple nodes with same XIDs / UIDs. Throw an error.
			err = errors.New(fmt.Sprintf("Found multiple nodes with ID: %s", qNameToUID[key]))
			gqlErr := schema.GQLWrapLocationf(
				err, mutation.Location(), "mutation %s failed", mutation.Name())
			return emptyResult(gqlErr), resolverFailed
		}
	}

	// Create upserts, delete mutations, update mutations, add mutations.
	upserts, err = mr.mutationRewriter.Rewrite(ctx, mutation, qNameToUID)

	if err != nil {
		return emptyResult(schema.GQLWrapf(err, "couldn't rewrite mutation %s", mutation.Name())),
			resolverFailed
	}
	if len(upserts) == 0 {
		return &Resolved{
			Data:       completeMutationResult(mutation, nil, 0),
			Field:      mutation,
			Err:        nil,

View on GitHub (pinned to 759e242be6)

Solutions

  1. Find and delete/merge the duplicate nodes sharing the same XID in the underlying Dgraph data
  2. Run a data cleanup query listing all nodes for the offending XID to identify duplicates
  3. Enforce XID uniqueness by re-adding the @id directive and cleaning legacy data
  4. Re-run the mutation after de-duplication

Example fix

// before
upsert(query: q(filter: { xid: { eq: "dup-1" } })) // matches 2 nodes
// after
cleanup duplicates so xid "dup-1" maps to exactly one node, then retry
Defensive patterns

Strategy: validation

Validate before calling

// before updating, verify uniqueness
const dupes = await dqlQuery(`query q(filter: { xid: { eq: "${xid}" } }) { count }`);
if (dupes.data.q.length > 1) throw new Error(`Found multiple nodes with ID: ${xid}`);

Try / catch

try {
  await updateMutation(input);
} catch (err) {
  if (err.message.startsWith('Found multiple nodes with ID:')) {
    // run dedup cleanup for the reported ID before retrying
  }
}

Prevention

When it happens

Trigger: An update/delete mutation whose @id/XID filter matches more than one stored node; concurrent writes created two nodes with the same external identifier; the uniqueness check (count > 1) fires in rewriteAndExecute.

Common situations: Data imported before the @id/XID uniqueness enforcement was added; manual DQL writes bypassing GraphQL validation; a race in concurrent Add mutations inserting the same XID twice.

Related errors


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