grafana/k6 · error
failed to decode X coordinate: %w
Error message
failed to decode X coordinate: %w
What it means
The x coordinate is decoded with base64.RawURLEncoding (base64url without padding). This error means the x string is not valid unpadded base64url: it contains '=', '+' or '/' characters, whitespace, or its length is impossible (length % 4 == 1). It is raised from crypto.subtle.importKey('jwk', ...) for ECDSA keys.
Source
Thrown at internal/js/modules/k6/webcrypto/jwk.go:238
func importECDSAJWK(_ EllipticCurveKind, jsonKeyData []byte) (any, CryptoKeyType, error) {
var jwkKey ecJWK
if err := json.Unmarshal(jsonKeyData, &jwkKey); err != nil {
return nil, UnknownCryptoKeyType, fmt.Errorf("failed to parse input as EC JWK key: %w", err)
}
if err := jwkKey.validate(); err != nil {
return nil, UnknownCryptoKeyType, fmt.Errorf("invalid EC JWK key: %w", err)
}
crv, err := pickEllipticCurve(jwkKey.Crv)
if err != nil {
return nil, UnknownCryptoKeyType, fmt.Errorf("failed to parse elliptic curve: %w", err)
}
x, err := base64URLDecode(jwkKey.X)
if err != nil {
return nil, UnknownCryptoKeyType, fmt.Errorf("failed to decode X coordinate: %w", err)
}
y, err := base64URLDecode(jwkKey.Y)
if err != nil {
return nil, UnknownCryptoKeyType, fmt.Errorf("failed to decode Y coordinate: %w", err)
}
pk := &ecdsa.PublicKey{
Curve: crv,
X: new(big.Int).SetBytes(x),
Y: new(big.Int).SetBytes(y),
}
// if the key is a public key, return it
if jwkKey.D == "" {
return pk, PublicCryptoKeyType, nil
}
View on GitHub (pinned to 93accf6570)
Solutions
- Convert x to unpadded base64url: replace '+' with '-', '/' with '_', and strip trailing '=' characters
- Re-encode the coordinate from the original raw bytes using base64url without padding
- Check for and remove whitespace/newlines inside the value
- Verify the decoded byte length matches the curve (32 bytes for P-256, 48 for P-384, 66 for P-521)
Example fix
// before
const jwk = { kty: 'EC', crv: 'P-256', x: b64X, y: b64Y }; // padded/standard base64
// after
const toB64u = (s) => s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '').trim();
const jwk = { kty: 'EC', crv: 'P-256', x: toB64u(b64X), y: toB64u(b64Y) }; Defensive patterns
Strategy: validation
Validate before calling
const B64URL = /^[A-Za-z0-9_-]+$/;
const toB64u = (s) => String(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '').trim();
const x = toB64u(jwk.x);
if (!B64URL.test(x) || x.length < 2) throw new Error('x is not valid unpadded base64url'); Type guard
function isB64uCoord(s) {
return typeof s === 'string' && /^[A-Za-z0-9_-]+$/.test(s) && s.length % 4 !== 1;
} Try / catch
try {
key = await crypto.subtle.importKey('jwk', jwk, alg, true, usages);
} catch (e) {
if (e.message.includes('failed to decode X coordinate')) {
jwk = { ...jwk, x: toB64u(jwk.x), y: toB64u(jwk.y) };
key = await crypto.subtle.importKey('jwk', jwk, alg, true, usages);
} else throw e;
} Prevention
- Normalize all JWK fields to unpadded base64url at load time
- Expect 43 chars for P-256 x, 64 for P-384, 88 for P-521
- Never paste coordinates with '=' padding or '+'/'/' characters
When it happens
Trigger: x encoded in standard base64 ('+/' alphabet) or padded base64url (trailing '='); x with trailing whitespace or newline from copy-paste; truncated x.
Common situations: JWKs produced by toolchains that emit padded or standard base64; coordinates copied from PEM/ASN.1 dumps; strings trimmed incorrectly during transport (JSON escaping issues).
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to decode Y coordinate: %w
- failed to decode D: %w
- failed to parse input as EC JWK key: %w
- invalid EC JWK key: %w
- failed to parse elliptic curve: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/f6cb634d9794fadd.
Report an issue: GitHub.