{"record":{"id":"a329476aab138b89","repo":"gchq/CyberChef","slug":"offset-cannot-be-negative-a32947","errorCode":null,"errorMessage":"Offset cannot be negative","messagePattern":"Offset cannot be negative","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/operations/MultipleBombe.mjs","lineNumber":220,"sourceCode":"                rstr = this.validateRotor(rstr);\n                fourthRotors.push(rstr);\n            }\n        }\n        if (fourthRotors.length === 0) {\n            fourthRotors.push(\"\");\n        }\n        for (const rstr of reflectorsStr.split(\"\\n\")) {\n            const reflector = new Reflector(rstr);\n            reflectors.push(reflector);\n        }\n        if (reflectors.length === 0) {\n            throw new OperationError(\"A minimum of one reflector must be supplied\");\n        }\n        if (crib.length === 0) {\n            throw new OperationError(\"Crib cannot be empty\");\n        }\n        if (offset < 0) {\n            throw new OperationError(\"Offset cannot be negative\");\n        }\n        // For symmetry with the Enigma op, for the input we'll just remove all invalid characters\n        input = input.replace(/[^A-Za-z]/g, \"\").toUpperCase();\n        crib = crib.replace(/[^A-Za-z]/g, \"\").toUpperCase();\n        const ciphertext = input.slice(offset);\n        let update;\n        if (isWorkerEnvironment()) {\n            update = this.updateStatus;\n        } else {\n            update = undefined;\n        }\n        let bombe = undefined;\n        const output = {bombeRuns: []};\n        // I could use a proper combinatorics algorithm here... but it would be more code to\n        // write one, and we don't seem to have one in our existing libraries, so massively nested\n        // for loop it is\n        const totalRuns = choose(rotors.length, 3) * 6 * fourthRotors.length * reflectors.length;\n        let nRuns = 0;","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/operations/MultipleBombe.mjs#L202-L238","documentation":"Thrown by MultipleBombe.run when the offset (args[5]) is less than zero. The offset marks where in the ciphertext the crib begins; a negative offset would slice the ciphertext backwards, which is meaningless, so it is rejected.","triggerScenarios":"run(input, args) where args[5] < 0. The argument is a number type; a negative value (e.g. -1) passed via the recipe or UI spinner trips this immediately.","commonSituations":"Spinner dragged below zero; recipe JSON edited to a negative offset; offset computed from an upstream operation that underflowed; sign error when computing crib position.","solutions":["Set the offset to 0 or a positive integer not exceeding the ciphertext length.","If computing the offset, clamp with Math.max(0, value).","Verify the crib's start position in the ciphertext."],"exampleFix":"// before\nchef.bake(\"Multiple Bombe\", [..., \"WETTER\", -5, true]);\n// after\nchef.bake(\"Multiple Bombe\", [..., \"WETTER\", 0, true]);","handlingStrategy":"validation","validationCode":"const offset = args[5];\nif (typeof offset !== \"number\" || offset < 0) {\n  // set offset to 0 or a positive integer\n}","typeGuard":"const isNonNegativeInt = (n) => Number.isInteger(n) && n >= 0;","tryCatchPattern":"try { chef.bake(\"Multiple Bombe\", args); }\ncatch (e) { if (e.message === \"Offset cannot be negative\") resetOffset(); else throw e; }","preventionTips":["Clamp computed offsets with Math.max(0, value).","Keep offset within ciphertext bounds.","Validate offset is a non-negative integer before baking."],"tags":["crypto","enigma","bombe","validation"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}