grafana/k6 · error
key (k) is required
Error message
key (k) is required
What it means
When importing a symmetric (octet, kty='oct') key from JWK via crypto.subtle.importKey, k6 unmarshals it into symmetricJWK and validates it (internal/js/modules/k6/webcrypto/jwk.go:37). The `k` member holds the base64url-encoded key material; if it is missing or an empty string, validate() returns "key (k) is required". (A wrong kty produces the separate "invalid key type" error.)
Source
Thrown at internal/js/modules/k6/webcrypto/jwk.go:43
// Set sets a key-value pair in the JWK.
func (jwk *JsonWebKey) Set(key string, value any) {
(*jwk)[key] = value
}
// symmetricJWK represents a symmetric JWK key.
// It is used to unmarshal symmetric keys from JWK format.
type symmetricJWK struct {
Kty string `json:"kty"`
K string `json:"k"`
}
func (jwk *symmetricJWK) validate() error {
if jwk.Kty != JWKOctKeyType {
return fmt.Errorf("invalid key type: %s", jwk.Kty)
}
if jwk.K == "" {
return errors.New("key (k) is required")
}
return nil
}
// extractSymmetricJWK extracts the symmetric key from a given JWK key (JSON data).
func extractSymmetricJWK(jsonKeyData []byte) ([]byte, error) {
sk := symmetricJWK{}
if err := json.Unmarshal(jsonKeyData, &sk); err != nil {
return nil, fmt.Errorf("failed to parse symmetric JWK: %w", err)
}
if err := sk.validate(); err != nil {
return nil, fmt.Errorf("invalid symmetric JWK: %w", err)
}
skBytes, err := base64URLDecode(sk.K)
if err != nil {View on GitHub (pinned to 93accf6570)
Solutions
- Include the base64url-encoded key material: { kty: 'oct', alg: 'A128GCM', k: base64urlKey }
- Verify programmatically that the JWK has a non-empty k before calling importKey
- Check how the JWK was serialized — a mismatched property name (e.g. 'key') silently drops k
Example fix
// before
const jwk = { kty: 'oct', alg: 'A256GCM' }; // no key material
const key = await crypto.subtle.importKey('jwk', jwk, { name: 'AES-GCM' }, true, ['encrypt']);
// after (base64url of the raw 32 bytes, no padding)
const jwk = { kty: 'oct', alg: 'A256GCM', k: 'RxS6T4LZf0p1O2n3M4q5R6s7T8u9V0w1X2y3Z4a5B6c' };
const key = await crypto.subtle.importKey('jwk', jwk, { name: 'AES-GCM' }, true, ['encrypt']); Defensive patterns
Strategy: validation
Validate before calling
const parsed = typeof jwk === 'string' ? JSON.parse(jwk) : jwk;
if (parsed.kty === 'oct' && (typeof parsed.k !== 'string' || parsed.k.length === 0)) {
throw new Error('symmetric JWK is missing the k (key material) member');
} Type guard
const isValidSymmetricJWK = (j) => j.kty === 'oct' && typeof j.k === 'string' && j.k.length > 0;
Try / catch
try {
key = await crypto.subtle.importKey('jwk', jwk, alg, true, usages);
} catch (e) {
if (String(e.message).includes('key (k) is required')) throw new Error('JWK has no key material — check how it was built/serialized');
throw e;
} Prevention
- Always include k (base64url, no padding) in oct JWKs
- Validate JWKs with a schema check before importKey
- Watch for property-name drift (k vs key) when building JWKs from config or secret stores
When it happens
Trigger: `crypto.subtle.importKey('jwk', { kty: 'oct', alg: 'A256GCM' }, ...)` — a JWK with no `k` field or `k: ''`. Common with hand-written or template JWKs where only the algorithm identifier was filled in.
Common situations: Building JWKs from configuration that forgot the key material; copying JWK examples and replacing alg but not k; string-building JSON where the k value ends up empty; secret-management code that injects the key under a different property name.
Related errors
- invalid EC JWK key: %w
- invalid RSA JWK key: %w
- keys are not valid elliptic curve keys
- keys have different curves {curve1} and {curve2}
- invalid key type: %s
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/e93f841fc41abdce.
Report an issue: GitHub.