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
- Provide exactly 13 disjoint pairs that collectively cover all 26 letters A-Z.
- Remove any self-stecker pairs (e.g. 'AA') from reflector input, since they are skipped and reduce the effective count.
- 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
- Reuse known-good historical reflector wirings (B, C) verbatim rather than retyping.
- Avoid self-stecker pairs in reflector specs since they are skipped and reduce coverage.
- Assert the covered-letter count is exactly 26 before constructing.
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
- ${name} connects ${pair[1]} more than once
- ${name} must be a whitespace-separated list of uppercase let
- ${name} connects ${pair[0]} more than once
- Rotor wiring must be 26 unique uppercase letters
- A minimum of three rotors must be supplied
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/ac68008d42bce7fd.
Report an issue: GitHub.