{"record":{"id":"49500af57745957d","repo":"gchq/CyberChef","slug":"starting-at-must-be-a-positive-or-zero-integer","errorCode":null,"errorMessage":"'Starting at' must be a positive or zero integer.","messagePattern":"'Starting at' must be a positive or zero integer\\.","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/operations/DropNthBytes.mjs","lineNumber":60,"sourceCode":"            }\n        ];\n    }\n\n    /**\n     * @param {byteArray} input\n     * @param {Object[]} args\n     * @returns {byteArray}\n     */\n    run(input, args) {\n        const n = args[0];\n        const start = args[1];\n        const eachLine = args[2];\n\n        if (parseInt(n, 10) !== n || n <= 0) {\n            throw new OperationError(\"'Drop every' must be a positive integer.\");\n        }\n        if (parseInt(start, 10) !== start || start < 0) {\n            throw new OperationError(\"'Starting at' must be a positive or zero integer.\");\n        }\n\n        let offset = 0;\n        const output = [];\n        for (let i = 0; i < input.length; i++) {\n            if (eachLine && input[i] === 0x0a) {\n                output.push(0x0a);\n                offset = i + 1;\n            } else if (i - offset < start || (i - (start + offset)) % n !== 0) {\n                output.push(input[i]);\n            }\n        }\n\n        return output;\n    }\n\n}\n","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/operations/DropNthBytes.mjs#L42-L78","documentation":"Thrown in DropNthBytes.run when the second argument start ('Starting at') fails parseInt(start,10) !== start || start < 0. The integer check rejects NaN and non-integers; start < 0 rejects negatives. Together they require a non-negative integer. Default is 0, so this fires only for an out-of-range value.","triggerScenarios":"The 'Starting at' field is a negative number, a fraction (e.g. 1.5), NaN, or a non-numeric string. Reachable via imported recipe JSON, a cleared UI field coercing to NaN, or the Node API with an unguarded computed start.","commonSituations":"Hand-edited recipe JSON with a negative offset; a UI clear leaving the field empty and resolving to NaN; programmatic use where start went negative.","solutions":["Set 'Starting at' to 0 or a positive whole number.","Programmatically, coerce with Math.max(0, Math.floor(start)).","Move the constraint into the Ingredient args metadata (min 0, integer) so the recipe validator enforces it pre-run.","Validate imported recipe JSON before loading."],"exampleFix":"// before\nconst args = [4, -1, false];          // throws\n// after\nconst args = [4, Math.max(0, Math.floor(start)), false];","handlingStrategy":"validation","validationCode":"function startingAt(s) {\n  if (!Number.isInteger(s) || s < 0) throw new RangeError(\"Starting at must be a non-negative integer\");\n  return s;\n}\nconst safeStart = startingAt(args[1]);","typeGuard":"const isNonNegativeInt = (v) => Number.isInteger(v) && v >= 0;","tryCatchPattern":null,"preventionTips":["Coerce computed start with Math.max(0, Math.floor(start)).","Declare min(0)/integer constraints in the Ingredient args metadata.","Validate imported recipe JSON before loading."],"tags":["input-validation","argument-validation","byte-manipulation","operation-error"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}