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 character

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Set Insertion, Deletion, and Substitution costs to 0 or a positive integer (0 is allowed and yields a 'free' edit).
  2. If costs are computed dynamically, clamp them with `Math.max(0, value)` before passing them as arguments.
  3. 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

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


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/392382d2b2bdf4bc. Report an issue: GitHub.