{"record":{"id":"ca98d125bcb9de51","repo":"ruvnet/ruflo","slug":"nodeid-must-be-16-lowercase-hex-chars","errorCode":null,"errorMessage":"nodeId must be 16 lowercase hex chars","messagePattern":"nodeId must be 16 lowercase hex chars","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/agentbbs-federation.ts","lineNumber":264,"sourceCode":" *\n * Blocks credentials-in-URL (they would be logged), and non-http schemes such\n * as `file:` which would turn a peer entry into a local file read.\n */\nexport function validatePeerUrl(raw: string): string {\n  let u: URL;\n  try { u = new URL(raw); } catch { throw new Error('peer url is not a valid URL'); }\n  if (u.protocol !== 'http:' && u.protocol !== 'https:') {\n    throw new Error('peer url must be http or https');\n  }\n  if (u.username || u.password) throw new Error('peer url must not embed credentials');\n  return u.origin;\n}\n\nexport function addPeer(\n  basePath: string,\n  input: { nodeId: string; url: string; publicKey: string; label?: string },\n): FederationPeer {\n  if (!NODE_ID_RE.test(input.nodeId ?? '')) throw new Error('nodeId must be 16 lowercase hex chars');\n  if (!HEX64_RE.test(input.publicKey ?? '')) throw new Error('publicKey must be 64 lowercase hex chars');\n  const url = validatePeerUrl(String(input.url));\n\n  const peers = readPeers(basePath);\n  if (peers.length >= MAX_PEERS) throw new Error(`peer registry is full (${MAX_PEERS})`);\n\n  const existing = peers.find(p => p.nodeId === input.nodeId);\n  if (existing) {\n    // Re-pinning a different key for a known nodeId is how a key-substitution\n    // attack would present. Require an explicit remove first.\n    if (existing.publicKey !== input.publicKey) {\n      throw new Error(`nodeId ${input.nodeId} is already pinned to a different publicKey; remove it first`);\n    }\n    existing.url = url;\n    if (input.label) existing.label = input.label;\n    writePeers(basePath, peers);\n    return existing;\n  }","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/ruvnet/ruflo/blob/2602b642d92234c710ffbe96bfb33007d481ceab/v3/@claude-flow/cli/src/mcp-tools/agentbbs-federation.ts#L246-L282","documentation":"addPeer validates every new peer's nodeId against NODE_ID_RE (16 lowercase hex chars) before writing it to the peer registry. This error means the supplied nodeId string is missing, mistyped, or formatted outside the required canonical identifier format, preventing registry entries keyed by untrusted identifiers.","triggerScenarios":"Calling addPeer(basePath, { nodeId: <value>, url, publicKey }) where input.nodeId fails NODE_ID_RE.test — e.g. undefined/null, uppercase hex, 15 or 17 chars, containing '-', '0x' prefixes, or non-hex characters.","commonSituations":"Passing the peer's human-readable name or label instead of its nodeId; copying an uppercased or decorated ID from logs; truncating/pasting the ID incorrectly; reading nodeId from an env var that is unset.","solutions":["Obtain the peer's actual nodeId (e.g. from the peer's getNodeIdentity output) and pass that exact string","Ensure it is exactly 16 lowercase hex characters matching /^[0-9a-f]{16}$/ — lowercase it and strip prefixes like '0x' or whitespace before calling","Check that the nodeId field is actually populated in your config/env source (not undefined)","If you generated the peer yourself, regenerate or read the identity file to recover its canonical nodeId"],"exampleFix":"// before\naddPeer(base, { nodeId: '0A1B-2C3D', url: 'https://p.example.com', publicKey: PK });\n// after\naddPeer(base, { nodeId: '0a1b2c3d4e5f6071', url: 'https://p.example.com', publicKey: PK });","handlingStrategy":"validation","validationCode":"const NODE_ID_RE = /^[0-9a-f]{16}$/;\nif (!NODE_ID_RE.test(nodeId)) {\n  throw new Error(`invalid nodeId '${nodeId}': must be exactly 16 lowercase hex chars`);\n}","typeGuard":"function isNodeId(v: unknown): v is string {\n  return typeof v === 'string' && /^[0-9a-f]{16}$/.test(v);\n}","tryCatchPattern":"try {\n  const peer = addPeer(basePath, input);\n} catch (e) {\n  if (e.message === 'nodeId must be 16 lowercase hex chars') {\n    console.error(`Peer '${input.label ?? input.nodeId}' has an invalid nodeId; fetch the peer's real nodeId via getNodeIdentity`);\n  } else throw e;\n}","preventionTips":["Copy nodeIds directly from the peer's identity file, not from logs or labels","Normalize before use: strip '0x', whitespace, separators, and lowercase the string","Validate config-supplied nodeIds with /^[0-9a-f]{16}$/ at startup","Never reuse a label or hostname as a nodeId"],"tags":["validation","identifier","format"],"backgroundTag":"invalid-identifier-format","analyzedSha":"2602b642d92234c710ffbe96bfb33007d481ceab","analyzedAt":"2026-09-15T22:58:14.805Z","contentChangedAt":"2026-09-15T22:58:14.805Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}