{"record":{"id":"2c232c4c6768e9c9","repo":"gchq/CyberChef","slug":"number-of-bytes-is-not-a-divisor-of-bytesperpixe","errorCode":null,"errorMessage":"Number of bytes is not a divisor of ${bytesPerPixel}","messagePattern":"Number of bytes is not a divisor of (.+?)","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/operations/GenerateImage.mjs","lineNumber":90,"sourceCode":"        const [mode, scale, width] = args;\n        input = new Uint8Array(input);\n\n        const bytePerPixelMap = {\n            Greyscale: 1,\n            RG: 2,\n            RGB: 3,\n            RGBA: 4,\n            Bits: 1 / 8,\n        };\n\n        if (!Object.hasOwn(bytePerPixelMap, mode)) {\n            throw new OperationError(`Unsupported Mode: (${mode})`);\n        }\n\n        const bytesPerPixel = bytePerPixelMap[mode];\n\n        if (bytesPerPixel > 0 && input.length % bytesPerPixel !== 0) {\n            throw new OperationError(\n                `Number of bytes is not a divisor of ${bytesPerPixel}`,\n            );\n        }\n\n        const height = Math.ceil(input.length / bytesPerPixel / width);\n        const image = new Jimp({ width, height });\n\n        if (isWorkerEnvironment())\n            self.sendStatusMessage(\"Generating image from data...\");\n\n        if (mode === \"Bits\") {\n            let index = 0;\n            for (let j = 0; j < input.length; j++) {\n                const curByte = Utils.bin(input[j]);\n                for (let k = 0; k < 8; k++, index++) {\n                    const x = index % width;\n                    const y = Math.floor(index / width);\n","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/operations/GenerateImage.mjs#L72-L108","documentation":"Thrown by GenerateImage when the input byte length is not evenly divisible by the bytes-per-pixel for the chosen mode. For RGB (3) the input length must be a multiple of 3; for RGBA (4) a multiple of 4, etc. This ensures every pixel is fully populated. The Bits mode (1/8 bytes/pixel) is skipped because bytesPerPixel is fractional (not > 0 is false for 1/8... actually 1/8 > 0 is true, so fractional modulo applies).","triggerScenarios":"Selecting RGB mode with 100 bytes of input (100 % 3 !== 0), or RGBA mode with a length that is not a multiple of 4. For Bits mode, the fractional check against 0.125 can also trigger for odd-length input because of floating-point modulo.","commonSituations":"Feeding raw binary data of arbitrary length without padding/truncating to the pixel boundary, or switching modes without adjusting input length.","solutions":["Pad or truncate the input so its byte length is a multiple of bytesPerPixel (1 for Greyscale/RG-ish, 2 for RG, 3 for RGB, 4 for RGBA).","Switch to Greyscale mode if you want every byte to map to a pixel.","Use Bits mode for bit-level packing, but ensure the length works with the fractional divisor."],"exampleFix":"// before: RGB mode, 100 bytes\ninput = buf(100); // 100 % 3 !== 0 -> error\n// after: trim to nearest multiple of 3\ninput = buf(99);","handlingStrategy":"validation","validationCode":"const BPP = { Greyscale: 1, RG: 2, RGB: 3, RGBA: 4 };\nconst bpp = BPP[mode];\nif (bpp && input.length % bpp !== 0) {\n  // pad or truncate input to a multiple of bpp before calling the operation\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pre-size input to an exact multiple of bytesPerPixel for the chosen mode.","Prefer Greyscale mode when input length is arbitrary."],"tags":["image","input-validation","user-input"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}