TheAlgorithms/Python · error · Exception

Please use 3 unique rotors (not {unique_rotsel})

Error message

Please use 3 unique rotors (not {unique_rotsel})

What it means

Raised by the Enigma machine's input validator (ciphers/enigma_machine2.py) when fewer than 3 unique rotors are selected: len(set(rotsel)) < 3. The historical machine needs three distinct physical rotors, so duplicates (e.g. [1, 2, 2] or [5, 5, 5]) are rejected. Note it raises bare Exception, not a more specific type.

Source

Thrown at ciphers/enigma_machine2.py:94

) -> tuple[RotorPositionT, RotorSelectionT, dict[str, str]]:
    """
    Checks if the values can be used for the ``enigma`` function

    >>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND')
    ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', \
'ZJXESIUQLHAVRMDOYGTNFWPBKC'), \
{'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'})

    :param rotpos: rotor_positon
    :param rotsel: rotor_selection
    :param pb: plugb -> validated and transformed
    :return: (`rotpos`, `rotsel`, `pb`)
    """
    # Checks if there are 3 unique rotors

    if (unique_rotsel := len(set(rotsel))) < 3:
        msg = f"Please use 3 unique rotors (not {unique_rotsel})"
        raise Exception(msg)

    # Checks if rotor positions are valid
    rotorpos1, rotorpos2, rotorpos3 = rotpos
    if not 0 < rotorpos1 <= len(abc):
        msg = f"First rotor position is not within range of 1..26 ({rotorpos1}"
        raise ValueError(msg)
    if not 0 < rotorpos2 <= len(abc):
        msg = f"Second rotor position is not within range of 1..26 ({rotorpos2})"
        raise ValueError(msg)
    if not 0 < rotorpos3 <= len(abc):
        msg = f"Third rotor position is not within range of 1..26 ({rotorpos3})"
        raise ValueError(msg)

    # Validates string and returns dict
    pbdict = _plugboard(pb)

    return rotpos, rotsel, pbdict

View on GitHub (pinned to f5988cc097)

Solutions

  1. Pass three distinct rotor selectors, e.g. rotsel=(1, 2, 3)
  2. If generating randomly, use random.sample(range(1, 6), 3) (without replacement)
  3. Check len(set(rotsel)) == 3 before calling the API

Example fix

# before
rotsel = (1, 2, 2)
enigma(rotpos, rotsel, pb)

# after
import random
rotsel = tuple(random.sample(range(1, 6), 3))  # 3 distinct rotors
enigma(rotpos, rotsel, pb)
Defensive patterns

Strategy: validation

Validate before calling

if len(set(rotsel)) < 3:
    raise ValueError("select 3 distinct rotors")

Type guard

def valid_rotor_selection(rotsel) -> bool:
    return len(set(rotsel)) == 3

Try / catch

try:
    settings = validate_and_transform(rotpos, rotsel, pb)
except Exception as exc:  # module raises bare Exception here
    if "unique rotors" in str(exc):
        rotsel = (1, 2, 3)  # fall back to distinct defaults
    else:
        raise

Prevention

When it happens

Trigger: Calling enigma with rotsel like (1, 2, 2), (3, 3, 3), or any tuple with duplicate rotor ids; passing strings vs ints inconsistently so set() collapses them ('1', 1) is not the issue here but duplicates are.

Common situations: Random rotor selection code that samples with replacement; UI defaults that repeat rotor I; refactoring from a 2-rotor toy example to the 3-rotor API.

Related errors


AI-assisted analysis of TheAlgorithms/Python@f5988cc097 (2026-08-14). Data as JSON: /api/errors/c1c28a84833b3a06. Report an issue: GitHub.