gchq/CyberChef · error · OperationError
Error opening image. (${err})
Error message
Error opening image. (${err}) What it means
Thrown by parseQrCode when `Jimp.read(input)` rejects — the input could not be decoded as a raster image. The underlying Jimp error is appended in parentheses. It is an OperationError surfaced to the recipe.
Source
Thrown at src/core/lib/QRCode.mjs:27
import OperationError from "../errors/OperationError.mjs";
import jsQR from "jsqr";
import qr from "qr-image";
import Utils from "../Utils.mjs";
import { Jimp, JimpMime } from "jimp";
/**
* Parses a QR code image from an image
*
* @param {ArrayBuffer} input
* @param {boolean} normalise
* @returns {string}
*/
export async function parseQrCode(input, normalise) {
let image;
try {
image = await Jimp.read(input);
} catch (err) {
throw new OperationError(`Error opening image. (${err})`);
}
try {
if (normalise) {
image.greyscale();
image.normalize();
}
} catch (err) {
throw new OperationError(`Error normalising image. (${err})`);
}
// Remove transparency which jsQR cannot handle
image.scan((x, y, idx) => {
// If pixel is fully transparent, make it opaque white
if (image.bitmap.data[idx + 3] === 0x00) {
image.bitmap.data[idx + 0] = 0xff;
image.bitmap.data[idx + 1] = 0xff;
image.bitmap.data[idx + 2] = 0xff;View on GitHub (pinned to 4290ea7539)
Solutions
- Ensure the input is a complete JPEG, PNG, or BMP ArrayBuffer.
- Pre-validate the magic bytes (e.g. PNG \x89PNG, JPEG \xff\xd8) before calling parseQrCode.
- If the source is SVG/WebP/HEIC, rasterise to PNG first.
Example fix
// before
await parseQrCode(textToArrayBuffer('not an image'), true);
// after
await parseQrCode(pngArrayBuffer, true); Defensive patterns
Strategy: validation
Validate before calling
function looksLikeImage(buf) {
const b = new Uint8Array(buf);
return (b[0] === 0x89 && b[1] === 0x50) // PNG
|| (b[0] === 0xff && b[1] === 0xd8) // JPEG
|| (b[0] === 0x42 && b[1] === 0x4d); // BMP
}
if (!looksLikeImage(input)) throw new Error('Input is not a supported image');
await parseQrCode(input, normalise); Type guard
function isSupportedImageBuffer(buf) {
const b = new Uint8Array(buf);
return (b[0] === 0x89 && b[1] === 0x50) || (b[0] === 0xff && b[1] === 0xd8) || (b[0] === 0x42 && b[1] === 0x4d);
} Try / catch
try {
return await parseQrCode(input, normalise);
} catch (e) {
if (e instanceof OperationError && /Error opening image/.test(e.message)) {
// convert/re-encode input to PNG before retrying
}
throw e;
} Prevention
- Check magic bytes before calling parseQrCode.
- Keep CyberChef/Jimp on supported formats (PNG/JPEG/BMP).
- Rasterise SVG/WebP/HEIC to PNG upstream.
When it happens
Trigger: Calling parseQrCode with an ArrayBuffer that is not a supported image format (Jimp supports JPEG, PNG, BMP, TIFF, GIF), a truncated/corrupt file, an empty buffer, or plain text bytes.
Common situations: Input not yet converted to an image (still raw bytes / SVG / WebP unsupported by the Jimp build); file truncated in transit; wrong MIME assumption; very large image exhausting memory.
Related errors
- Error normalising image. (${err})
- Could not read a QR code from the image.
- Unsupported QR code format.
- Invalid file type.
- Invalid file type.
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/dcb46a9048edcd30.
Report an issue: GitHub.