dgraph-io/dgraph · critical

REUSE_RAFTID: Healthy connection to a member with same ID: %

Error message

REUSE_RAFTID: Healthy connection to a member with same ID: %+v

What it means

During Connect, Zero found an existing member with the same Raft ID as the requester but a different address, and that old member's address still has a healthy gRPC connection. Zero concludes the ID genuinely belongs to a live node elsewhere, so a new connection with the same ID is rejected to protect Raft uniqueness. (Message assembled across two Go string literals.)

Source

Thrown at dgraph/cmd/zero/zero.go:576

			case member.Addr == m.Addr && m.Id == 0:
				glog.Infof("Found a member with the same address. Returning: %+v", member)
				conn.GetPools().Connect(m.Addr, s.tlsClientConfig)
				return &pb.ConnectionState{
					State:  ms,
					Member: member,
				}, nil

			case member.Addr == m.Addr && member.Id != m.Id:
				// Same address. Different Id. If Id is zero, then it might be trying to connect for
				// the first time. We can just directly return the membership information.
				return nil, errors.Errorf("REUSE_ADDR: Duplicate address to existing member: %+v."+
					" Self: +%v", member, m)

			case member.Addr != m.Addr && member.Id == m.Id:
				// Same Id. Different address.
				if pl, err := conn.GetPools().Get(member.Addr); err == nil && pl.IsHealthy() {
					// Found a healthy connection.
					return nil, errors.Errorf("REUSE_RAFTID: Healthy connection to a member"+
						" with same ID: %+v", member)
				}
			}
			numberOfNodes++
		}
	}

	// Create a connection and check validity of the address by doing an Echo.
	conn.GetPools().Connect(m.Addr, s.tlsClientConfig)

	createProposal := func() *pb.ZeroProposal {
		s.Lock()
		defer s.Unlock()

		proposal := new(pb.ZeroProposal)
		// Check if we already have this member.
		for _, group := range s.state.Groups {
			if _, has := group.Members[m.Id]; has {

View on GitHub (pinned to 759e242be6)

Solutions

  1. Identify which instance truly owns the Raft ID and shut down or wipe the impostor's raft (p) state so it gets a fresh ID.
  2. If the node legitimately moved addresses, first remove the old member (RemoveNode) or let Zero expire it, then reconnect.
  3. Stop assigning static/duplicated --raft "id" values; let nodes auto-assign IDs with clean state.
  4. Check Zero /state to see the conflicting member and its address, then fix the deployment accordingly.

Example fix

// before: cloned image carries id=2 while original a1 (id=2) is still healthy
// after on the clone:
rm -rf p && dgraph alpha --my=a2:7080 --zero=z1:5080
Defensive patterns

Strategy: validation

Validate before calling

state := fetchZeroState("http://zero:6080/state")
for _, groups := range state.Groups {
    for id, m := range groups.Members {
        if id == myRaftID && m.Addr != myAddr {
            return fmt.Errorf("raft id %d is live at %s; wipe local raft state or pick new id", id, m.Addr)
        }
    }
}

Try / catch

_, err := zc.Connect(ctx, m)
if err != nil && strings.Contains(err.Error(), "REUSE_RAFTID") && strings.Contains(err.Error(), "Healthy connection") {
    return fmt.Errorf("another live member already owns raft id %d; shut down the impostor: %w", m.Id, err)
}

Prevention

When it happens

Trigger: Connect with m.Id equal to a current member's Id while m.Addr differs, and conn.GetPools().Get(member.Addr) succeeds and pl.IsHealthy() is true — i.e., two hosts simultaneously claiming the same Raft ID.

Common situations: Cloned VM/container images with copied raft state; a node moved to a new address without removing its old membership; a stale/recovering node reconnecting while its replacement is already healthy; misconfigured static raft IDs in fleet provisioning.

Related errors


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