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.)

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.

Appendix: 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)