{"record":{"id":"451eb827addc342d","repo":"ruvnet/ruflo","slug":"cannot-select-from-empty-array","errorCode":null,"errorMessage":"Cannot select from empty array","messagePattern":"Cannot select from empty array","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/shared/src/security/secure-random.ts","lineNumber":142,"sourceCode":"  const maxValid = Math.pow(256, bytesNeeded) - (Math.pow(256, bytesNeeded) % range);\n\n  let randomValue: number;\n  do {\n    const bytes = randomBytes(bytesNeeded);\n    randomValue = bytes.reduce((acc, byte, i) => acc + byte * Math.pow(256, i), 0);\n  } while (randomValue >= maxValid);\n\n  return min + (randomValue % range);\n}\n\n/**\n * Secure random selection from array\n * @param array Array to select from\n * @returns Random element\n */\nexport function secureRandomChoice<T>(array: T[]): T {\n  if (array.length === 0) {\n    throw new Error('Cannot select from empty array');\n  }\n  return array[secureRandomInt(0, array.length - 1)]!;\n}\n\n/**\n * Secure shuffle array (Fisher-Yates with crypto)\n * @param array Array to shuffle\n * @returns New shuffled array\n */\nexport function secureShuffleArray<T>(array: T[]): T[] {\n  const result = [...array];\n  for (let i = result.length - 1; i > 0; i--) {\n    const j = secureRandomInt(0, i);\n    [result[i], result[j]] = [result[j]!, result[i]!];\n  }\n  return result;\n}\n","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/shared/src/security/secure-random.ts#L124-L160","documentation":"secureRandomChoice() picks one uniformly random element of an array via the crypto-backed secureRandomInt(). An empty array has no element to return; rather than yielding undefined (and letting modulo selection run on a zero-length range), it throws this guard immediately.","triggerScenarios":"Passing [] produced by a filter that matched nothing (e.g. candidate nodes filtered by capability or health); an optional config list that defaulted to empty; an array emptied earlier by pop/splice; choosing from a peers/providers list that was never populated.","commonSituations":"Random load balancing over a 'healthy nodes' list when every node failed its health check; random secret/token generation configured with an empty charset; consensus or scheduling code choosing from zero candidates after membership removal.","solutions":["Check array.length > 0 before calling secureRandomChoice.","Fix the upstream producer so the candidate set is non-empty (health checks, config defaults, membership).","Fall back to a known non-empty default list when the primary candidate set is empty."],"exampleFix":"// before\nconst target = secureRandomChoice(healthyNodes); // throws when healthyNodes is []\n\n// after\nif (healthyNodes.length === 0) {\n  throw new Error('no healthy nodes available');\n}\nconst target = secureRandomChoice(healthyNodes);","handlingStrategy":"validation","validationCode":"if (!Array.isArray(candidates) || candidates.length === 0) {\n  throw new Error('candidate list must be non-empty');\n}\nconst pick = secureRandomChoice(candidates);","typeGuard":"function isNonEmpty<T>(a: T[]): a is [T, ...T[]] {\n  return a.length > 0;\n}","tryCatchPattern":"try {\n  pick = secureRandomChoice(candidates);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Cannot select from empty array') {\n    pick = fallbackCandidate; // or recompute the candidate set\n  } else {\n    throw e;\n  }\n}","preventionTips":["Treat an empty candidate set as a domain error where it is produced (health checks, config loading), not at the random call.","Type lists as non-empty tuples ([T, ...T[]]) where the invariant holds, pushing the check to construction time.","Log when candidate sets become empty — it usually indicates an upstream failure being masked."],"tags":["security","random","crypto","input-validation"],"backgroundTag":"empty-array-access","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}