gchq/CyberChef · error · OperationError
Negative costs are not allowed.
Error message
Negative costs are not allowed.
What it means
Thrown by the Levenshtein Distance operation when any of the Insertion, Deletion, or Substitution cost arguments is negative. The dynamic-programming algorithm relies on non-negative weights to guarantee a meaningful minimum edit cost; negative weights would make the optimum ill-defined. The guard is on line 62-64 of src/core/operations/LevenshteinDistance.mjs.
Source
Thrown at src/core/operations/LevenshteinDistance.mjs:63
type: "number",
value: 1
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {number}
*/
run(input, args) {
const [delim, insCost, delCost, subCost] = args;
const samples = input.split(delim);
if (samples.length !== 2) {
throw new OperationError("Incorrect number of samples. Check your input and/or delimiter.");
}
if (insCost < 0 || delCost < 0 || subCost < 0) {
throw new OperationError("Negative costs are not allowed.");
}
const src = samples[0], dest = samples[1];
let currentCost = new Array(src.length + 1);
let nextCost = new Array(src.length + 1);
for (let i = 0; i < currentCost.length; i++) {
currentCost[i] = delCost * i;
}
for (let i = 0; i < dest.length; i++) {
const destc = dest.charAt(i);
nextCost[0] = currentCost[0] + insCost;
for (let j = 0; j < src.length; j++) {
let candidate;
// insertion
let optCost = currentCost[j + 1] + insCost;
// deletion
candidate = nextCost[j] + delCost;
if (candidate < optCost) optCost = candidate;
// substitution or matched characterView on GitHub (pinned to 4290ea7539)
Solutions
- Set Insertion, Deletion, and Substitution costs to 0 or a positive integer (0 is allowed and yields a 'free' edit).
- If costs are computed dynamically, clamp them with `Math.max(0, value)` before passing them as arguments.
- Validate the three cost arguments against `value >= 0 && Number.isFinite(value)` before invoking the operation.
Example fix
// before: args = ["\n", 1, -1, 1] -> throws 'Negative costs are not allowed.' // after: args = ["\n", 1, 1, 1] (or 0 allowed) -> ok
Defensive patterns
Strategy: validation
Validate before calling
function validateCosts(insCost, delCost, subCost) {
for (const [name, v] of [["insCost", insCost], ["delCost", delCost], ["subCost", subCost]]) {
if (!Number.isFinite(v) || v < 0) {
throw new Error(`${name} must be a finite number >= 0 (got ${v})`);
}
}
}
// then: validateCosts(args[1], args[2], args[3]); Type guard
function isNonNegativeCost(v) {
return typeof v === "number" && Number.isFinite(v) && v >= 0;
} Try / catch
try {
const result = levenshtein.run(input, args);
} catch (err) {
if (err instanceof OperationError && /Negative costs/.test(err.message)) {
args = [args[0], Math.max(0, args[1]), Math.max(0, args[2]), Math.max(0, args[3])];
// retry with clamped costs
} else {
throw err;
}
} Prevention
- Clamp externally computed costs with Math.max(0, value) before passing them in.
- Treat edit costs as non-negative integers by convention in your recipe/UI.
- Validate all three cost fields against `Number.isFinite(v) && v >= 0` before run().
When it happens
Trigger: Calling LevenshteinDistance.run(input, args) where insCost, delCost, or subCost (args[1], args[2], args[3]) is less than 0. Defaults are all 1; a manually entered negative value (e.g. -1) triggers the error.
Common situations: A typo entering '-1' instead of '1'; attempting to 'reward' certain edits with negative weights (not supported); a UI/recipe that computed costs from user data without clamping; copying a recipe whose number field lost its sign.
Related errors
- Incorrect number of samples. Check your input and/or delimit
- Ψ1 start must be between 1 and 43
- Ψ2 start must be between 1 and 47
- Ψ3 start must be between 1 and 51
- Ψ4 start must be between 1 and 53
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/392382d2b2bdf4bc.
Report an issue: GitHub.