gchq/CyberChef · error · OperationError

L must be non-negative

Error message

L must be non-negative

What it means

Thrown by Derive HKDF Key run() when L (the requested output length in octets) is negative. HKDF (RFC 5869) defines L as a non-negative integer; the operation's UI sets min:0 on the number argument, but a programmatic call or a hand-edited recipe can pass a negative value. The guard runs before any HKDF computation.

Source

Thrown at src/core/operations/DeriveHKDFKey.mjs:112

    }

    /**
     * @param {ArrayBuffer} input
     * @param {Object[]} args
     * @returns {ArrayBuffer}
     */
    run(input, args) {
        const argSalt = Utils.convertToByteString(args[0].string || "", args[0].option),
            info = Utils.convertToByteString(args[1].string || "", args[1].option),
            hashFunc = args[2].toLowerCase(),
            extractMode = args[3],
            L = args[4],
            IKM = Utils.arrayBufferToStr(input, false),
            hasher = CryptoApi.getHasher(hashFunc),
            HashLen = hasher.finalize().length;

        if (L < 0) {
            throw new OperationError("L must be non-negative");
        }
        if (L > 255 * HashLen) {
            throw new OperationError("L too large (maximum length for " + args[2] + " is " + (255 * HashLen) + ")");
        }

        const hmacHash = function(key, data) {
            hasher.reset();
            const mac = CryptoApi.getHmac(key, hasher);
            mac.update(data);
            return mac.finalize();
        };
        const salt = extractMode === "with salt" ? argSalt : "\0".repeat(HashLen);
        const PRK = extractMode === "skip" ? IKM : hmacHash(salt, IKM);
        let T = "";
        let result = "";
        for (let i = 1; i <= 255 && result.length < L; i++) {
            const TNext = hmacHash(PRK, T + info + String.fromCharCode(i));
            result += TNext;

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Set L to a non-negative value (0 returns an empty string, typical KDF lengths are 16/32/64).
  2. Validate the length upstream before invoking run().
  3. If the UI allows a negative value to slip through, report it as a UI min-enforcement bug; meanwhile clamp to 0.

Example fix

// before
const args = [salt, info, "SHA256", "with salt", -16];
op.run(input, args); // throws

// after
const args = [salt, info, "SHA256", "with salt", 16];
op.run(input, args);
Defensive patterns

Strategy: validation

Validate before calling

function isValidHkdfLength(L) {
    return Number.isInteger(L) && L >= 0;
}

Type guard

/** @returns {boolean} */
function isNonNegativeInt(L) {
    return Number.isInteger(L) && L >= 0;
}

Try / catch

try {
    out = deriveHkdfKey.run(input, args);
} catch (e) {
    if (e instanceof OperationError && /L must be non-negative/.test(e.message)) {
        args[4] = Math.max(0, Math.floor(args[4]));
        out = deriveHkdfKey.run(input, args);
    } else throw e;
}

Prevention

When it happens

Trigger: Passing args[4] < 0 to run(). Possible via programmatic invocation, an imported recipe with a bad number, or a UI bug that allows a negative number through despite min:0.

Common situations: Automation/Node-API passing an unvalidated length; a recipe file with a corrupted L value; chaining an operation whose numeric output can be negative into this op's L argument.

Related errors


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