gchq/CyberChef · warning · OperationError

Rotor steps must be unique

Error message

Rotor steps must be unique

What it means

Thrown by the Rotor constructor when, after normalising step letters against the ring setting, the resulting Set of step positions is smaller than the input steps string length — i.e. two distinct step letters map to the same turnover position. The comment notes this isn't strictly fatal but is treated as a probable mistake.

Source

Thrown at src/core/lib/Enigma.mjs:125

        const uniq = {};
        for (let i=0; i<LETTERS.length; i++) {
            const a = a2i(LETTERS[i]);
            const b = a2i(wiring[i]);
            this.map[a] = b;
            this.revMap[b] = a;
            uniq[b] = true;
        }
        if (Object.keys(uniq).length !== LETTERS.length) {
            throw new OperationError("Rotor wiring must have each letter exactly once");
        }
        const rs = a2i(ringSetting);
        this.steps = new Set();
        for (const x of steps) {
            this.steps.add(Utils.mod(a2i(x) - rs, 26));
        }
        if (this.steps.size !== steps.length) {
            // This isn't strictly fatal, but it's probably a mistake
            throw new OperationError("Rotor steps must be unique");
        }
        this.pos = Utils.mod(a2i(initialPosition) - rs, 26);
    }

    /**
     * Step the rotor forward by one.
     */
    step() {
        this.pos = Utils.mod(this.pos + 1, 26);
        return this.pos;
    }

    /**
     * Transform a character through this rotor forwards.
     *
     * @param {number} c - The character.
     * @returns {number}
     */

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Remove duplicate letters from the steps string.
  2. Confirm steps has no repeated characters before constructing (new Set(steps).size === steps.length).
  3. Use step letters directly from the canonical ROTORS definitions.

Example fix

// before
new Rotor('EKMFLGDQVZNTOWYHXUSPAIBRCJ', 'RR', 'A', 'A'); // duplicate R

// after
new Rotor('EKMFLGDQVZNTOWYHXUSPAIBRCJ', 'R', 'A', 'A');
Defensive patterns

Strategy: validation

Validate before calling

function hasUniqueStepLetters(steps) {
  return typeof steps === "string" && new Set(steps).size === steps.length;
}
if (!hasUniqueStepLetters(steps)) {
  throw new Error("Rotor steps contain duplicate letters.");
}

Type guard

function hasUniqueChars(s) {
  return typeof s === "string" && new Set(s).size === s.length;
}

Prevention

When it happens

Trigger: new Rotor(wiring, steps, ringSetting, ...) where two step letters differ but Utils.mod(a2i(x) - rs, 26) collide. Also fires trivially when steps contains a repeated letter (e.g. 'RR'), since steps.length counts chars but Set dedupes.

Common situations: Duplicate letters in the steps string; passing a steps value derived from a malformed ROTORS entry; accidentally concatenating two rotor step strings.

Related errors


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