gchq/CyberChef · error · OperationError
Letter ${letter} is not included in LS47
Error message
Letter ${letter} is not included in LS47 What it means
Thrown by the internal findIx helper in LS47.mjs when a character is not present in the LS47 tile alphabet. LS47 uses a fixed 49-character 7x7 grid: `_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()`. findIx linearly scans the tiles array; a character outside that set, or any lookup before tiles have been populated, throws. OperationError.
Source
Thrown at src/core/lib/LS47.mjs:72
* @returns {string}
*/
function rotateRight(key, row, n) {
const mid = key.slice(row * 7, (row + 1) * 7);
n = (7 - n % 7) % 7;
return key.slice(0, 7 * row) + mid.slice(n) + mid.slice(0, n) + key.slice(7 * (row + 1));
}
/**
* Finds the position of a letter in the tiles.
*
* @param {string} letter
* @returns {string}
*/
function findIx(letter) {
for (let i = 0; i < tiles.length; i++)
if (tiles[i][0] === letter)
return tiles[i][1];
throw new OperationError("Letter " + letter + " is not included in LS47");
}
/**
* Derives key from the input password.
*
* @param {string} password
* @returns {string}
*/
export function deriveKey(password) {
let i = 0;
let k = letters;
for (const c of password) {
const [row, col] = findIx(c);
k = rotateDown(rotateRight(k, i, col), i, row);
i = (i + 1) % 7;
}
return k;
}View on GitHub (pinned to 4290ea7539)
Solutions
- Call initTiles() once before any encrypt/decrypt/deriveKey call.
- Sanitise input so every character is in the LS47 alphabet (lowercase the text; strip or reject unsupported symbols/whitespace).
- Validate the input string against the alphabet before processing.
Example fix
// before
initTiles(); // forgotten, or input has uppercase
deriveKey("P@ssword");
// after
import { initTiles } from "./LS47.mjs";
initTiles();
const ALPHABET = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()";
const clean = [..."Password"].map(c => ALPHABET.includes(c.toLowerCase()) ? c.toLowerCase() : "").join("");
deriveKey(clean); Defensive patterns
Strategy: validation
Validate before calling
import { initTiles } from "./LS47.mjs";
initTiles(); // MUST run before any encrypt/decrypt/deriveKey
const LS47_ALPHABET = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()";
function sanitizeLS47(input) {
let out = "";
for (const ch of input) {
const lower = ch.toLowerCase();
const candidate = LS47_ALPHABET.includes(lower) ? lower : (LS47_ALPHABET.includes(ch) ? ch : null);
if (candidate !== null) out += candidate;
}
return out;
}
const clean = sanitizeLS47(userInput);
deriveKey(clean); Type guard
const isInLS47Alphabet = ch => LS47_ALPHABET.includes(ch); const isLS47Safe = str => [...str].every(isInLS47Alphabet);
Try / catch
try {
deriveKey(password);
} catch (err) {
if (err instanceof OperationError && /is not included in LS47/.test(err.message)) {
// input contained an out-of-alphabet char; sanitize and retry
} else throw err;
} Prevention
- Always call initTiles() once before any LS47 operation when using the library standalone.
- Lowercase input and strip unsupported symbols/whitespace before processing.
- Validate every char against the 49-char LS47 alphabet before calling encrypt/decrypt/deriveKey.
When it happens
Trigger: Calling encrypt/decrypt/deriveKey with input or a key/password containing characters outside the LS47 alphabet (uppercase letters, '@', '#', '%', '&', space, newline, tabs), OR calling any LS47 function before `initTiles()` has run (tiles is empty, so every lookup throws).
Common situations: Feeding mixed-case or punctuation-rich text; pasting text with whitespace/newlines; forgetting to call initTiles() when using the library standalone; key derived from user input that includes unsupported symbols.
Related errors
- Invalid Base64 alphabet length (${alphabet.length}): ${alpha
- Error: Base64 alphabet should be 64 characters long, or 65 w
- Wrong key size
- Letter ${elem} not in LS47
- Letter duplicated in the key
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/e46025211a22b87a.
Report an issue: GitHub.