ErrLookup › Background articles › OperationError in CyberChef — "Unknown algorithm version", "Invalid encoding", "Error loading image": one error class behind every operation failure

OperationError in CyberChef — "Unknown algorithm version", "Invalid encoding", "Error loading image": one error class behind every operation failure

OperationError is the single error class CyberChef throws for every operation failure — "Unknown algorithm version", "Invalid encoding", "Error loading image. (${err})", "Invalid key length", and hundreds more. A developer meets it when a recipe or chef.bake / Recipe.run call passes an argument that misses a constrained option list, supplies malformed or truncated input, or triggers an underlying decoder or cipher that rejects the data. This article explains the shared mechanism behind the messages and the fixes that hold across the whole family.

Distilled from 585 documented records across 2 repositories.

Background

CyberChef is built from operations, and every operation contracts to one of two outcomes: it returns its output, or it throws an OperationError. The class is the universal error channel for the operation layer; it is what surfaces in the browser output pane and what a Node-API caller catches from chef.bake or Recipe.run. The host never sees a raw exception from a dependency — operations catch their internals and re-throw as OperationError so a bad input cannot crash the recipe. In the documented family the thrown text takes one of two shapes. The first is a hand-written precondition or validation guard written for that operation: "Unknown algorithm version: ${version}", "Invalid encoding", "The values of a and b can only be integers.", "Plugboard wiring must be 26 unique uppercase letters". The second is a catch-all wrapper that interpolates the underlying library's own message through a template: "Error loading image. (${err})", "Error: ${err.message}", "${err.toString()}", "${error}". The wrapper shape deliberately collapses many internal failure modes into one surfaced message, so the real diagnostic is often the text inside the ${...} placeholder rather than the fixed prose around it.

Why the class exists is tied to how operations receive their arguments. In the browser UI most arguments come from constrained dropdowns (an argSelector or option list), so a value that the operation would reject is effectively unreachable — the user can only pick a declared option. The same guards become live as soon as the operation is driven another way: the Node API, an imported .chr recipe JSON, or a hand-edited opList can pass any string or number, and the operation's internal switch or regex then falls through to its default case and throws. A large share of the documented messages — "Unknown algorithm version", "Unknown input format", "Lorenz model type not recognised", "Unrecognised unit", "Invalid encoding" — exist almost exclusively for this programmatic path, because a label has drifted by case, spelling, trailing space, or CyberChef version.

From the caller's side the family has a consistent feel even when the underlying cause differs. Input-shape failures (wrong charset, wrong length, a letter reused in a bijective map, a non-integer where modular arithmetic expects one) carry precise, hand-written messages that name the violated constraint. Binary-length failures ("Need at least 20 bytes for a TCP Header", "Incorrect input length. Must be a multiple of the block size.", "Invalid key length: ${key.length} bytes") are precondition guards that fire before any real parsing, often because an earlier step truncated the data or the wrong input format was selected. Decode-dependent operations (DitherImage, OpticalCharacterRecognition, AsconDecrypt, BcryptCompare, Argon2, Protobuf Encode) wrap a third-party library and forward its rejection, frequently after only a shallow pre-check — CyberChef's isImage sniff validates magic bytes, not structural integrity, so a header-valid but body-corrupt file still reaches the decoder and fails there.

The documented family is large — 585 records across two repositories — but the best-documented records are all from gchq/CyberChef, and within CyberChef the pattern above is uniform: one class, two message shapes, and a consistent split between hand-written guards and interpolated wrappers. Where the same OperationError name appears in the second repository, message text, trigger conditions, and recoverability are library-specific rather than shared; the cross-family lessons here are the structural ones — normalisation of internal failures, argument provenance, and validation at the trust boundary — not the literal message strings.

Common causes

What usually fixes it

Go deeper

Documented occurrences

…and 565 more across the corpus — use search.

Honest provenance: generated on 2026-08-13 from AI-assisted analysis of the linked records. See how records are made.