gchq/CyberChef · error · OperationError

Rotor wiring must be 26 unique uppercase letters

Error message

Rotor wiring must be 26 unique uppercase letters

What it means

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.

Source

Thrown at src/core/operations/MultipleBombe.mjs:170

        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`;
        self.sendStatusMessage(msg);
    }

    /**
     * Early rotor description string validation.
     * Drops stepping information.
     * @param {string} rstr - The rotor description string
     * @returns {string} - Rotor description with stepping stripped, if any
     */
    validateRotor(rstr) {
        // The Bombe doesn't take stepping into account so we'll just ignore it here
        if (rstr.includes("<")) {
            rstr = rstr.split("<", 2)[0];
        }
        // Duplicate the validation of the rotor strings here, otherwise you might get an error
        // thrown halfway into a big Bombe run
        if (!/^[A-Z]{26}$/.test(rstr)) {
            throw new OperationError("Rotor wiring must be 26 unique uppercase letters");
        }
        if (new Set(rstr).size !== 26) {
            throw new OperationError("Rotor wiring must be 26 unique uppercase letters");
        }
        return rstr;
    }

    /**
     * @param {string} input
     * @param {Object[]} args
     * @returns {string}
     */
    run(input, args) {
        const mainRotorsStr = args[1];
        const fourthRotorsStr = args[2];
        const reflectorsStr = args[3];
        let crib = args[4];
        const offset = args[5];

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Supply exactly 26 uppercase A-Z characters per rotor line, e.g. 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.
  2. Strip any label/prefix and uppercase the string before pasting.
  3. Use the canonical Wehrmacht/Naval rotor definitions from the operation's default values.

Example fix

// before
EKMFLGDQVZNTOWYHXUSPAIBRC   // 25 letters -> regex fail
// after
EKMFLGDQVZNTOWYHXUSPAIBRCJ  // 26 letters
Defensive patterns

Strategy: validation

Validate before calling

function validRotorShape(rstr) {
  if (rstr.includes("<")) rstr = rstr.split("<", 2)[0];
  return /^[A-Z]{26}$/.test(rstr);
}

Type guard

const isRotorShaped = (rstr) =>
  /^[A-Z]{26}$/.test(rstr.includes("<") ? rstr.split("<", 2)[0] : rstr);

Try / catch

try { chef.bake("Multiple Bombe", args); }
catch (e) { if (e.message.startsWith("Rotor wiring must be")) fixRotorLines(); else throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/b83293d53b51540d. Report an issue: GitHub.