gchq/CyberChef · error · OperationError

Reflector must have exactly 13 pairs covering every letter

Error message

Reflector must have exactly 13 pairs covering every letter

What it means

Thrown by the Reflector constructor after PairMapBase finishes building its map. A reflector must be a complete involution: every one of the 26 letters A-Z must be paired with exactly one partner. The constructor counts connected letters (`Object.keys(this.map).length`) and rejects anything other than 26. Fewer than 13 disjoint pairs (or pairs that were silently skipped as self-stecker) cause the count to fall short.

Source

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

        return this.transform(c);
    }
}

/**
 * Reflector. PairMapBase but requires that all characters are accounted for.
 *
 * Includes a couple of optimisations on that basis.
 */
export class Reflector extends PairMapBase {
    /**
     * Reflector constructor. See PairMapBase.
     * Additional restriction: every character must be accounted for.
     */
    constructor(pairs) {
        super(pairs, "Reflector");
        const s = Object.keys(this.map).length;
        if (s !== 26) {
            throw new OperationError("Reflector must have exactly 13 pairs covering every letter");
        }
        const optMap = new Array(26);
        for (const x of Object.keys(this.map)) {
            optMap[x] = this.map[x];
        }
        this.map = optMap;
    }

    /**
     * Transform a character through this object.
     *
     * @param {number} c - The character.
     * @returns {number}
     */
    transform(c) {
        return this.map[c];
    }
}

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Provide exactly 13 disjoint pairs that collectively cover all 26 letters A-Z.
  2. Remove any self-stecker pairs (e.g. 'AA') from reflector input, since they are skipped and reduce the effective count.
  3. Validate with a uniqueness/completeness check before constructing the Reflector.

Example fix

// before
new Reflector("AB CD EF GH IJ KL MN OP QR ST UV"); // 22 letters

// after (reflects every letter)
new Reflector("AY BR CU DH EQ FS GL IP JX KN MO TZ VW");
Defensive patterns

Strategy: validation

Validate before calling

function validateReflector(pairs) {
  const covered = new Set();
  for (const pair of pairs.split(/\s+/)) {
    if (!/^[A-Z]{2}$/.test(pair)) throw new Error(`Bad pair: ${pair}`);
    if (pair[0] === pair[1]) continue; // skipped, does not count
    covered.add(pair[0]); covered.add(pair[1]);
  }
  if (covered.size !== 26) throw new Error(`Reflector covers ${covered.size}/26 letters; need exactly 13 disjoint pairs`);
}
validateReflector(reflectorStr);
new Reflector(reflectorStr);

Type guard

function isCompleteReflector(pairs) {
  const covered = new Set();
  for (const pair of pairs.split(/\s+/)) {
    if (!/^[A-Z]{2}$/.test(pair)) return false;
    if (pair[0] === pair[1]) continue;
    if (covered.has(pair[0]) || covered.has(pair[1])) return false;
    covered.add(pair[0]); covered.add(pair[1]);
  }
  return covered.size === 26;
}

Try / catch

try {
  new Reflector(reflectorStr);
} catch (err) {
  if (err instanceof OperationError && /exactly 13 pairs/.test(err.message)) {
    // prompt user to supply a complete reflector wiring
  } else throw err;
}

Prevention

When it happens

Trigger: Calling `new Reflector("AB CD EF GH IJ KL MN OP QR ST")` (only 10 pairs / 20 letters), including self-stecker pairs like "AA" which do not populate the map, or supplying a duplicate-letter set that PairMapBase already accepted but left fewer than 26 distinct entries.

Common situations: Partial reflector wiring copied from a source that omitted pairs; accidental inclusion of 'AA'-style entries in a reflector spec; hand-typing a known reflector (B/C) and missing a pair.

Related errors


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