{"record":{"id":"ff2dc769bb74125a","repo":"ruvnet/ruflo","slug":"node-agentid-already-exists-in-topology","errorCode":null,"errorMessage":"Node ${agentId} already exists in topology","messagePattern":"Node (.+?) already exists in topology","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/swarm/src/topology-manager.ts","lineNumber":69,"sourceCode":"      this.config = { ...this.config, ...config };\n      this.state.type = this.config.type;\n    }\n\n    this.emit('initialized', { type: this.config.type });\n  }\n\n  getState(): TopologyState {\n    return {\n      ...this.state,\n      nodes: [...this.state.nodes],\n      edges: [...this.state.edges],\n      partitions: [...this.state.partitions],\n    };\n  }\n\n  async addNode(agentId: string, role: TopologyNode['role']): Promise<TopologyNode> {\n    if (this.nodeIndex.has(agentId)) {\n      throw new Error(`Node ${agentId} already exists in topology`);\n    }\n\n    if (this.nodeIndex.size >= this.config.maxAgents) {\n      throw new Error(`Maximum agents (${this.config.maxAgents}) reached`);\n    }\n\n    // Create node with connections based on topology type\n    const connections = this.calculateInitialConnections(agentId, role);\n\n    const node: TopologyNode = {\n      id: `node_${agentId}`,\n      agentId,\n      role: this.determineRole(role),\n      status: 'syncing',\n      connections,\n      metadata: {\n        joinedAt: new Date().toISOString(),\n        version: '1.0.0',","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/swarm/src/topology-manager.ts#L51-L87","documentation":"TopologyManager.addNode() indexes nodes by agentId in nodeIndex; adding an agentId that is already present throws before any connections, edges, or partitions are touched. The guard keeps the graph consistent because a duplicate id would corrupt adjacency lists. No partial state is written when it throws.","triggerScenarios":"Calling addNode('worker-1', 'worker') twice; a rejoin handler that calls addNode on every reconnect without a prior removeNode; retry loops re-running registration after a timeout where the first add actually succeeded.","commonSituations":"Agent crash-restart logic that treats connect as add; at-least-once delivery of registration messages; test suites that reuse a shared TopologyManager across cases without teardown.","solutions":["Call removeNode(agentId) first (and let auto-rebalance settle) before re-adding a rejoining agent","Make registration idempotent: check the manager's current nodes for the agentId before adding","Scope one TopologyManager per test with proper teardown so ids never leak between cases"],"exampleFix":"// before\nawait topology.addNode(agentId, 'worker'); // throws on rejoin: already exists\n\n// after\nconst exists = topology.getState().nodes.some(n => n.agentId === agentId);\nif (exists) {\n  await topology.removeNode(agentId);\n}\nawait topology.addNode(agentId, 'worker');","handlingStrategy":"validation","validationCode":"function nodeExists(topology: TopologyManager, agentId: string): boolean {\n  return topology.getState().nodes.some(n => n.agentId === agentId);\n}\n\nif (nodeExists(topology, agentId)) {\n  await topology.removeNode(agentId); // rejoin: replace the old node\n}\nawait topology.addNode(agentId, 'worker');","typeGuard":"function isKnownAgentId(topology: TopologyManager, agentId: string): boolean {\n  return topology.getState().nodes.some(n => n.agentId === agentId);\n}","tryCatchPattern":"try {\n  await topology.addNode(agentId, 'worker');\n} catch (err) {\n  if (err instanceof Error && err.message.endsWith('already exists in topology')) {\n    await topology.removeNode(agentId);\n    return topology.addNode(agentId, 'worker');\n  }\n  throw err;\n}","preventionTips":["Treat agent join as an upsert in one helper (remove-then-add or check-then-add)","Always await addNode so retry loops know whether the first attempt succeeded","Log agentId on every add and remove to spot double registration in reconnect logs"],"tags":["topology","duplicate-entry","add-node","agent-registration"],"backgroundTag":"duplicate-entry","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}