{"record":{"id":"7a3ec24cf8c5f162","repo":"ruvnet/ruflo","slug":"no-eligible-peers-for-exploration","errorCode":null,"errorMessage":"no eligible peers for exploration","messagePattern":"no eligible peers for exploration","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/browser/src/application/mcts-explorer.ts","lineNumber":88,"sourceCode":"    this.peers = options.peers;\n    this.scorer = options.scorer;\n    this.maxDepth = options.maxDepth ?? 5;\n    this.maxBranches = options.maxBranches ?? 32;\n    this.defaultPeerBudgetUsd = options.defaultPeerBudgetUsd ?? 1.0;\n    this.ucb = options.ucb ?? { c: Math.SQRT2 };\n    this.trustedPublicKeys = options.trustedPublicKeys ?? [];\n  }\n\n  /** Explore from a seed root action. Returns the winning branch's ID + aggregate stats. */\n  async explore(input: {\n    rootAction: RootAction;\n    goal: string;\n    expansionPolicy: ExpansionPolicy;\n  }): Promise<MctsRunResult> {\n    const runId = 'run-' + Date.now() + '-' + randomBytes(3).toString('hex');\n    const rootId = 'br-root-' + randomBytes(3).toString('hex');\n    const rootPeer = this.pickPeer();\n    if (!rootPeer) throw new Error('no eligible peers for exploration');\n\n    const root: McTsBranch = McTsBranchSchema.parse({\n      id: rootId,\n      runId,\n      parentId: null,\n      peerId: rootPeer.id,\n      action: input.rootAction.action,\n      input: input.rootAction.input,\n      depth: 0,\n      visits: 0,\n      totalValue: 0,\n      status: 'pending',\n      costUsd: 0,\n      createdAt: new Date().toISOString(),\n    });\n    this.branches.set(rootId, root);\n\n    // Execute and expand iteratively. Counter is # of EXECUTIONS (not # of","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/browser/src/application/mcts-explorer.ts#L70-L106","documentation":"Thrown by MctsExplorer.explore() at the very start of a run when pickPeer() returns null. pickPeer filters peers against the peerBlacklist set, so this fires when every peer passed to the constructor has since been blacklisted. The constructor already rejects an empty peers array with a different message, so this specific error means peers existed at construction but none are now eligible (budget exhausted, bad signatures, or a prior run poisoned the explorer's mutable state).","triggerScenarios":"Calling explorer.explore() on an explorer instance that has already completed a run where every peer was blacklisted (budget exhausted or invalid trajectory signatures). Also fired when constructing the explorer with peers whose budgetUsd is 0, or when reusing a single MctsExplorer instance across runs because peerBlacklist, peerSpend, and peerSignatureBlacklist are instance fields that accumulate across explore() calls.","commonSituations":"Sharing one MctsExplorer across many runs without realizing the blacklist is never cleared; setting defaultPeerBudgetUsd to 0; a federation where all peers return Ed25519 signatures not in trustedPublicKeys (so they get signature-blacklisted after the first call).","solutions":["Construct a fresh MctsExplorer (new MctsExplorer(options)) for each explore() call so blacklist/spend state does not leak between runs.","Before exploring, verify options.peers has length > 0 and that at least one peer has budgetUsd > 0 (or defaultPeerBudgetUsd > 0).","If using trustedPublicKeys, ensure every peer signs with a key in that list; otherwise leave trustedPublicKeys unset to accept any valid signature.","Raise defaultPeerBudgetUsd or each peer's budgetUsd so a single run cannot exhaust all peers."],"exampleFix":"// before\nconst explorer = new MctsExplorer({ peers, scorer });\nawait explorer.explore({ rootAction, goal, expansionPolicy });\nawait explorer.explore({ rootAction: another, goal, expansionPolicy }); // throws 60\n\n// after — fresh explorer per run\nfunction runMcts(peers, scorer, input) {\n  const explorer = new MctsExplorer({ peers, scorer });\n  return explorer.explore(input);\n}","handlingStrategy":"validation","validationCode":"function hasEligiblePeer(opts) {\n  return Array.isArray(opts.peers)\n    && opts.peers.length > 0\n    && opts.peers.some(p => (p.budgetUsd ?? opts.defaultPeerBudgetUsd ?? 1.0) > 0);\n}\n// before explore:\nif (!hasEligiblePeer(options)) throw new Error('configure at least one peer with positive budget');","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Construct a new MctsExplorer per run; blacklist/spend state is instance-scoped and never reset.","Pin trustedPublicKeys to the keys your peers actually sign with, or leave it empty to accept any valid signature.","Set defaultPeerBudgetUsd high enough that one run cannot exhaust every peer."],"tags":["mcts","federation","budget","state-leak","typescript"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}