{"record":{"id":"b83293d53b51540d","repo":"gchq/CyberChef","slug":"rotor-wiring-must-be-26-unique-uppercase-letters-b83293","errorCode":null,"errorMessage":"Rotor wiring must be 26 unique uppercase letters","messagePattern":"Rotor wiring must be 26 unique uppercase letters","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/operations/MultipleBombe.mjs","lineNumber":170,"sourceCode":"        const msg = `Bombe run with ${nLoops} loop${nLoops === 1 ? \"\" : \"s\"} in menu (2+ desirable): ${nStops} stops, ${Math.floor(100 * progress)}% done, ${hours}:${minutes}:${seconds} remaining`;\n        self.sendStatusMessage(msg);\n    }\n\n    /**\n     * Early rotor description string validation.\n     * Drops stepping information.\n     * @param {string} rstr - The rotor description string\n     * @returns {string} - Rotor description with stepping stripped, if any\n     */\n    validateRotor(rstr) {\n        // The Bombe doesn't take stepping into account so we'll just ignore it here\n        if (rstr.includes(\"<\")) {\n            rstr = rstr.split(\"<\", 2)[0];\n        }\n        // Duplicate the validation of the rotor strings here, otherwise you might get an error\n        // thrown halfway into a big Bombe run\n        if (!/^[A-Z]{26}$/.test(rstr)) {\n            throw new OperationError(\"Rotor wiring must be 26 unique uppercase letters\");\n        }\n        if (new Set(rstr).size !== 26) {\n            throw new OperationError(\"Rotor wiring must be 26 unique uppercase letters\");\n        }\n        return rstr;\n    }\n\n    /**\n     * @param {string} input\n     * @param {Object[]} args\n     * @returns {string}\n     */\n    run(input, args) {\n        const mainRotorsStr = args[1];\n        const fourthRotorsStr = args[2];\n        const reflectorsStr = args[3];\n        let crib = args[4];\n        const offset = args[5];","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/operations/MultipleBombe.mjs#L152-L188","documentation":"Thrown by MultipleBombe.validateRotor when a rotor wiring string (after stripping any '<stepping>' suffix) does not match /^[A-Z]{26}$/. Each Enigma rotor must be a permutation expressed as exactly 26 uppercase ASCII letters. Failing this regex means wrong length, lowercase, digits, or non-letters.","triggerScenarios":"validateRotor(rstr) called on any line of the main-rotors or fourth-rotor text areas where the line is not exactly 26 chars of A-Z (e.g. 25 letters, lowercase, contains a digit, contains a space, or empty line). The check fires early so a malformed rotor fails before a long Bombe run.","commonSituations":"Typo in a hand-entered rotor; pasting rotors that include a leading label like 'I:'; trailing carriage returns producing a 27-char line; lowercase paste from a documentation site; missing one letter.","solutions":["Supply exactly 26 uppercase A-Z characters per rotor line, e.g. 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.","Strip any label/prefix and uppercase the string before pasting.","Use the canonical Wehrmacht/Naval rotor definitions from the operation's default values."],"exampleFix":"// before\nEKMFLGDQVZNTOWYHXUSPAIBRC   // 25 letters -> regex fail\n// after\nEKMFLGDQVZNTOWYHXUSPAIBRCJ  // 26 letters","handlingStrategy":"validation","validationCode":"function validRotorShape(rstr) {\n  if (rstr.includes(\"<\")) rstr = rstr.split(\"<\", 2)[0];\n  return /^[A-Z]{26}$/.test(rstr);\n}","typeGuard":"const isRotorShaped = (rstr) =>\n  /^[A-Z]{26}$/.test(rstr.includes(\"<\") ? rstr.split(\"<\", 2)[0] : rstr);","tryCatchPattern":"try { chef.bake(\"Multiple Bombe\", args); }\ncatch (e) { if (e.message.startsWith(\"Rotor wiring must be\")) fixRotorLines(); else throw e; }","preventionTips":["Always paste canonical rotor definitions.","Strip labels and uppercase before pasting.","Validate each line is exactly 26 A-Z chars."],"tags":["crypto","enigma","bombe","validation"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}