gchq/CyberChef · error · OperationError

M61 custom lugs must be 61 long and can only include . or x

Error message

M61 custom lugs must be 61 long and can only include . or x

What it means

Thrown by the Lorenz operation only when 'Wheel Pattern' is 'Custom'. The Μ61 motor wheel has 61 cams, so its lug pattern must be exactly 61 characters matching ^[.xX]*$. Guard is on line 288 of src/core/operations/Lorenz.mjs.

Source

Thrown at src/core/operations/Lorenz.mjs:288

        if (m37<1 || m37>37) throw new OperationError("Μ37 start must be between 1 and 37");
        if (m61<1 || m61>61) throw new OperationError("Μ61 start must be between 1 and 61");
        if (x1<1 || x1>41) throw new OperationError("Χ1 start must be between 1 and 41");
        if (x2<1 || x2>31) throw new OperationError("Χ2 start must be between 1 and 31");
        if (x3<1 || x3>29) throw new OperationError("Χ3 start must be between 1 and 29");
        if (x4<1 || x4>26) throw new OperationError("Χ4 start must be between 1 and 26");
        if (x5<1 || x5>23) throw new OperationError("Χ5 start must be between 1 and 23");

        // Initialise chosen wheel pattern
        let chosenSetting = "";
        if (pattern === "Custom") {
            const re = new RegExp("^[.xX]*$");
            if (lugs1.length !== 43 || !re.test(lugs1)) throw new OperationError("Ψ1 custom lugs must be 43 long and can only include . or x ");
            if (lugs2.length !== 47 || !re.test(lugs2)) throw new OperationError("Ψ2 custom lugs must be 47 long and can only include . or x");
            if (lugs3.length !== 51 || !re.test(lugs3)) throw new OperationError("Ψ3 custom lugs must be 51 long and can only include . or x");
            if (lugs4.length !== 53 || !re.test(lugs4)) throw new OperationError("Ψ4 custom lugs must be 53 long and can only include . or x");
            if (lugs5.length !== 59 || !re.test(lugs5)) throw new OperationError("Ψ5 custom lugs must be 59 long and can only include . or x");
            if (lugm37.length !== 37 || !re.test(lugm37)) throw new OperationError("M37 custom lugs must be 37 long and can only include . or x");
            if (lugm61.length !== 61 || !re.test(lugm61)) throw new OperationError("M61 custom lugs must be 61 long and can only include . or x");
            if (lugx1.length !== 41 || !re.test(lugx1)) throw new OperationError("Χ1 custom lugs must be 41 long and can only include . or x");
            if (lugx2.length !== 31 || !re.test(lugx2)) throw new OperationError("Χ2 custom lugs must be 31 long and can only include . or x");
            if (lugx3.length !== 29 || !re.test(lugx3)) throw new OperationError("Χ3 custom lugs must be 29 long and can only include . or x");
            if (lugx4.length !== 26 || !re.test(lugx4)) throw new OperationError("Χ4 custom lugs must be 26 long and can only include . or x");
            if (lugx5.length !== 23 || !re.test(lugx5)) throw new OperationError("Χ5 custom lugs must be 23 long and can only include . or x");
            chosenSetting = INIT_PATTERNS["No Pattern"];
            chosenSetting.S[1] = this.readLugs(lugs1);
            chosenSetting.S[2] = this.readLugs(lugs2);
            chosenSetting.S[3] = this.readLugs(lugs3);
            chosenSetting.S[4] = this.readLugs(lugs4);
            chosenSetting.S[5] = this.readLugs(lugs5);
            chosenSetting.M[1] = this.readLugs(lugm61);
            chosenSetting.M[2] = this.readLugs(lugm37);
            chosenSetting.X[1] = this.readLugs(lugx1);
            chosenSetting.X[2] = this.readLugs(lugx2);
            chosenSetting.X[3] = this.readLugs(lugx3);
            chosenSetting.X[4] = this.readLugs(lugx4);
            chosenSetting.X[5] = this.readLugs(lugx5);

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Provide a Μ61 lugs string of exactly 61 characters using only '.', 'x', 'X'.
  2. Edit a known-good 61-character pattern in place rather than retyping.
  3. Pre-validate with /^[.xX]{61}$/ before running.

Example fix

// before: args[25] = ".xxxx" (5 chars) -> throws
// after:  args[25] = ".xxxx.xxxx.xxx.xxxx.xx....xxx.xxxx.xxxx.xxxx.xxxx.xxx.xxxx..."  (61 chars) -> ok
Defensive patterns

Strategy: validation

Validate before calling

// Μ61 lugs (args[25]) must match /^[.xX]{61}$/ when pattern === 'Custom'.
function validateCustomLugs(str, size, name) {
  if (typeof str !== "string" || !new RegExp(`^[.xX]{${size}}$`).test(str)) {
    throw new Error(`${name} lugs must be exactly ${size} chars of '.', 'x', or 'X'`);
  }
}
if (args[1] === "Custom") validateCustomLugs(args[25], 61, "M61");

Type guard

const isMu61Lugs = (s) => typeof s === "string" && /^[.xX]{61}$/.test(s);

Try / catch

try {
  lorenz.run(input, args);
} catch (err) {
  if (err instanceof OperationError && /M61 custom lugs/.test(err.message)) {
    args[25] = ".xxxx.xxxx.xxx.xxxx.xx....xxx.xxxx.xxxx.xxxx.xxxx.xxx.xxxx..."; // known-good 61-char pattern
  } else throw err;
}

Prevention

When it happens

Trigger: Calling Lorenz.run() with pattern='Custom' where args[25] (Μ61 lugs) is not exactly 61 characters or contains characters other than '.', 'x', 'X'.

Common situations: Custom pattern with a Μ61 string truncated (e.g. to 60 chars); a value copied from Μ37 (37 chars); an invalid character introduced during editing; trailing newline counted as a character.

Related errors


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