{"record":{"id":"8403dbf79f71b4fe","repo":"gchq/CyberChef","slug":"name-connects-pair-1-more-than-once","errorCode":null,"errorMessage":"${name} connects ${pair[1]} more than once","messagePattern":"(.+?) connects (.+?) more than once","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/Enigma.mjs","lineNumber":191,"sourceCode":"        this.pairs = pairs;\n        this.map = {};\n        if (pairs === \"\") {\n            return;\n        }\n        pairs.split(/\\s+/).forEach(pair => {\n            if (!/^[A-Z]{2}$/.test(pair)) {\n                throw new OperationError(name + \" must be a whitespace-separated list of uppercase letter pairs\");\n            }\n            const a = a2i(pair[0]), b = a2i(pair[1]);\n            if (a === b) {\n                // self-stecker\n                return;\n            }\n            if (Object.prototype.hasOwnProperty.call(this.map, a)) {\n                throw new OperationError(`${name} connects ${pair[0]} more than once`);\n            }\n            if (Object.prototype.hasOwnProperty.call(this.map, b)) {\n                throw new OperationError(`${name} connects ${pair[1]} more than once`);\n            }\n            this.map[a] = b;\n            this.map[b] = a;\n        });\n    }\n\n    /**\n     * Transform a character through this object.\n     * Returns other characters unchanged.\n     *\n     * @param {number} c - The character.\n     * @returns {number}\n     */\n    transform(c) {\n        if (!Object.prototype.hasOwnProperty.call(this.map, c)) {\n            return c;\n        }\n        return this.map[c];","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/Enigma.mjs#L173-L209","documentation":"Thrown by PairMapBase (the shared base for Enigma Plugboard and Reflector) during construction. The pairs string is split on whitespace and each two-letter pair wires two letters together bijectively; a letter may only appear in a single pair. This specific throw fires on the SECOND letter of a new pair when that letter was already wired by a previous pair (e.g. \"AB CB\" reuses B). It reports the offending letter via the component name passed in (\"Plugboard\"/\"Reflector\").","triggerScenarios":"Constructing `new Plugboard(\"AB CB\")`, `new Reflector(\"AB BC BD ...\")`, or any PairMapBase subclass where a letter recurs as the second element of a later pair. Self-stecker pairs like \"AA\" are silently skipped (early return) and do NOT seed the map, so they never trigger this.","commonSituations":"Typo or copy-paste duplication in a plugboard/reflector recipe argument; transcribing a historical reflector wiring and repeating a letter; mixing two stecker tables together without de-duplicating.","solutions":["Scan the pairs string for any letter that appears in more than one non-self pair and remove the duplicate.","Re-verify against the intended wiring table (every letter A-Z at most once, except self-stecker pairs like 'AA' which are no-ops).","Build pairs programmatically and assert uniqueness before passing to the constructor."],"exampleFix":"// before\nnew Plugboard(\"AB CD BF\"); // B wired twice (AB and BF)\n\n// after\nnew Plugboard(\"AB CD EF\"); // each letter appears once","handlingStrategy":"validation","validationCode":"function validatePairs(pairs, name = \"PairMapBase\") {\n  if (pairs === \"\") return;\n  const seen = new Set();\n  for (const pair of pairs.split(/\\s+/)) {\n    if (!/^[A-Z]{2}$/.test(pair)) throw new Error(`${name} pair '${pair}' is not two uppercase letters`);\n    if (pair[0] === pair[1]) continue; // self-stecker, skipped by lib\n    for (const ch of pair) {\n      if (seen.has(ch)) throw new Error(`${name} connects ${ch} more than once`);\n      seen.add(ch);\n    }\n  }\n}\nvalidatePairs(plugboardStr, \"Plugboard\");\nnew Plugboard(plugboardStr);","typeGuard":"function isUniquePairs(pairs) {\n  if (pairs === \"\") return true;\n  const seen = new Set();\n  for (const pair of pairs.split(/\\s+/)) {\n    if (!/^[A-Z]{2}$/.test(pair)) return false;\n    if (pair[0] === pair[1]) continue;\n    for (const ch of pair) { if (seen.has(ch)) return false; seen.add(ch); }\n  }\n  return true;\n}","tryCatchPattern":"try {\n  new Plugboard(plugboardStr);\n} catch (err) {\n  if (err instanceof OperationError && /connects .* more than once/.test(err.message)) {\n    // surface to user: duplicate letter in pairs\n  } else throw err;\n}","preventionTips":["Generate plugboard/reflector pairs from a single source-of-truth table rather than hand-typing.","Run a uniqueness assertion over the pairs string before construction.","Remember self-stecker pairs like 'AA' are silently ignored and do not count toward coverage."],"tags":["enigma","plugboard","reflector","validation","duplicate","configuration"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}