grafana/k6 · error
keys have different curves {curve1} and {curve2}
Error message
keys have different curves {curve1} and {curve2} What it means
The second check in ensureKeysUseSameCurve (internal/js/modules/k6/webcrypto/elliptic_curve.go:728) compares the NamedCurve of the two ECDH keys passed to deriveBits/deriveKey. ECDH mathematically requires both keys to be on the same curve; mixing curves (message includes both, e.g. "keys have different curves P-256 and P-384") returns this error before any bits are derived.
Source
Thrown at internal/js/modules/k6/webcrypto/elliptic_curve.go:729
return nil, fmt.Errorf("unable to convert ECDH public key to ECDSA public key, curve: %s", crv.Params().Name)
}
return &ecdsa.PublicKey{
Curve: crv,
X: x,
Y: y,
}, nil
}
func ensureKeysUseSameCurve(k1, k2 CryptoKey) error {
ecAlg1, ok1 := k1.Algorithm.(EcKeyAlgorithm)
ecAlg2, ok2 := k2.Algorithm.(EcKeyAlgorithm)
if !ok1 || !ok2 {
return errors.New("keys are not valid elliptic curve keys")
}
if ecAlg1.NamedCurve != ecAlg2.NamedCurve {
return errors.New("keys have different curves " + string(ecAlg1.NamedCurve) + " and " + string(ecAlg2.NamedCurve))
}
return nil
}
View on GitHub (pinned to 93accf6570)
Solutions
- Make the namedCurve identical on both sides — regenerate or re-import so both use the same curve
- Determine the peer's curve first (from its JWK crv or SPKI parameters) and generate your ECDH key with the same namedCurve
- Centralize the curve choice in one constant used by both generateKey and importKey paths
Example fix
// before
const mine = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveBits']);
const peerPub = await crypto.subtle.importKey('jwk', serverJwk, { name: 'ECDH', namedCurve: 'P-384' }, true, []);
await crypto.subtle.deriveBits({ name: 'ECDH', public: peerPub }, mine.privateKey, 256);
// after (match the peer's curve)
const peerPub = await crypto.subtle.importKey('jwk', serverJwk, { name: 'ECDH', namedCurve: 'P-384' }, true, []);
const mine = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-384' }, true, ['deriveBits']);
await crypto.subtle.deriveBits({ name: 'ECDH', public: peerPub }, mine.privateKey, 256); Defensive patterns
Strategy: validation
Validate before calling
const curveOf = (k) => k.algorithm.namedCurve;
if (curveOf(privKey) !== curveOf(publicKey)) {
throw new Error(`curve mismatch: ${curveOf(privKey)} vs ${curveOf(publicKey)} — regenerate on the same curve`);
} Type guard
const sameCurve = (a, b) => a.algorithm?.namedCurve != null && a.algorithm.namedCurve === b.algorithm?.namedCurve;
Try / catch
try {
bits = await crypto.subtle.deriveBits({ name: 'ECDH', public: peerPub }, priv, 256);
} catch (e) {
if (String(e.message).includes('different curves')) throw new Error(`ECDH keys must share one curve (${e.message})`);
throw e;
} Prevention
- Use a single CURVE constant for generateKey and every importKey of peer keys
- Read the peer JWK crv (or SPKI parameters) and align your key's namedCurve to it
- Validate curve equality in a shared deriveKey() wrapper used across the script
When it happens
Trigger: `crypto.subtle.deriveBits({ name: 'ECDH', public: pub384 }, priv256, 256)` — private key generated on P-256 while the peer public key (imported from JWK/raw or generated) is on P-384 or P-521.
Common situations: Hard-coded curve in the script ('P-256') while the server/peer publishes keys on P-384; keys imported from different environments (prod vs staging) with different crypto policies; copying example keys from docs generated on another curve.
Related errors
- keys are not valid elliptic curve keys
- invalid ECDH curve
- key (k) is required
- invalid elliptic curve {k}
- curve not supported for converting to ECDSA key
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/94a74c2279bdece8.
Report an issue: GitHub.