gchq/CyberChef · error · OperationError

Letter ${letter} is not in the key

Error message

Letter ${letter} is not in the key

What it means

findPos(key, letter) maps a plaintext/ciphertext character to a [row, col] coordinate on the 7x7 key tile grid. If the character is not present in the key string (indexOf === -1) or the index is out of the 0..48 range, it throws. Because the valid key is a permutation of the LS47 alphabet, this effectively rejects any input character outside "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()".

Source

Thrown at src/core/lib/LS47.mjs:123

            throw new OperationError("Letter " + elem + " not in LS47");
        counts[elem]++;
        if (counts[elem] > 1)
            throw new OperationError("Letter duplicated in the key");
    }
}

/**
 * Finds the position of a letter in they key.
 *
 * @param {letter} key
 * @param {string} letter
 * @returns {object}
 */
function findPos (key, letter) {
    const index = key.indexOf(letter);
    if (index >= 0 && index < 49)
        return [Math.floor(index/7), index%7];
    throw new OperationError("Letter " + letter + " is not in the key");
}

/**
 * Returns the character at the position on the tiles.
 *
 * @param {string} key
 * @param {object} coord
 * @returns {string}
 */
function findAtPos(key, coord) {
    return key.charAt(coord[1] + (coord[0] * 7));
}

/**
 * Returns new position by adding two positions.
 *
 * @param {object} a
 * @param {object} b

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Sanitize input to only LS47-alphabet characters before encrypting: input.replace(/[^_a-z0-9.,\-+*/:?!'()]/g, '').
  2. Lowercase any alphabetic input and substitute or strip unsupported punctuation.
  3. Verify the ciphertext comes from the same LS47 alphabet before calling decrypt.
  4. Confirm you are using the correct key and that the ciphertext was not transformed by another operation.

Example fix

// before
const ct = LS47.encrypt(key, "Hello World"); // 'H',' ','W' not in alphabet

// after
const clean = plaintext.toLowerCase().replace(/[^_a-z0-9.,\-+*/:?!'()]/g, '');
const ct = LS47.encrypt(key, clean);
Defensive patterns

Strategy: validation

Validate before calling

const LS47_ALPHABET = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()";

function sanitizeLS47Input(s) {
  return s.toLowerCase().replace(/[^_a-z0-9.,\-+*/:?!'()]/g, '');
}

const clean = sanitizeLS47Input(plaintext);
const ct = LS47.encrypt(key, clean);

Type guard

function isLS47AlphabetString(x): x is string {
  const A = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()";
  return typeof x === 'string' && [...x].every(c => A.includes(c));
}

Try / catch

try {
  return LS47.encrypt(key, plaintext);
} catch (e) {
  if (e instanceof OperationError && /is not in the key/.test(e.message)) {
    throw new Error('Input contains a character outside the LS47 alphabet: ' + e.message);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling encrypt(key, plaintext) with a plaintext containing uppercase letters, '@', newlines, or any char not in the 49-char alphabet. Calling decrypt(key, ciphertext) with a ciphertext containing such a char (typically because the ciphertext was corrupted or the wrong key was used to partially decode).

Common situations: User feeds raw prose containing spaces or uppercase letters directly to encrypt(); input not lowercased/normalized; ciphertext mangled by transport (URL-decoding, copy-paste through formatting); wrong operation chained before LS47 decrypt.

Related errors


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