grafana/k6 · error
invalid symmetric JWK: %w
Error message
invalid symmetric JWK: %w
What it means
Umbrella error thrown by k6 WebCrypto when a symmetric JWK fails semantic validation after parsing. extractSymmetricJWK (internal/js/modules/k6/webcrypto/jwk.go:56-58) wraps symmetricJWK.validate's error with 'invalid symmetric JWK: %w'; the inner error is either 'invalid key type: <kty>' (kty is not 'oct') or 'key (k) is required' (the k field is missing/empty). The message therefore nests the real cause after the colon.
Source
Thrown at internal/js/modules/k6/webcrypto/jwk.go:57
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 {
return nil, fmt.Errorf("failed to decode symmetric key: %w", err)
}
return skBytes, nil
}
// exportSymmetricJWK exports a symmetric key as a map of JWK key parameters.
func exportSymmetricJWK(key *CryptoKey) (*JsonWebKey, error) {
rawKey, ok := key.handle.([]byte)
if !ok {
return nil, errors.New("key's handle isn't a byte slice")
}
// wrap result into the object that is expected to be returnedView on GitHub (pinned to 93accf6570)
Solutions
- Read the nested cause: if it says 'key (k) is required', add the base64url-encoded secret as k.
- If it says 'invalid key type', see that kty is exactly 'oct' (lowercase).
- Use correct JSON field names: kty, k — both lowercase.
Example fix
// before
await crypto.subtle.importKey('jwk', { kty: 'oct' }, { name: 'AES-GCM' }, false, ['encrypt']);
// after
await crypto.subtle.importKey('jwk', { kty: 'oct', k: base64urlSecret }, { name: 'AES-GCM' }, false, ['encrypt']); Defensive patterns
Strategy: validation
Validate before calling
function assertValidOctJwk(jwk) {
if (jwk.kty !== 'oct') throw new Error(`expected kty 'oct', got '${jwk.kty}'`);
if (!jwk.k || typeof jwk.k !== 'string') throw new Error('oct JWK requires a non-empty k field');
} Type guard
const isValidOctJwk = (jwk) => jwk != null && jwk.kty === 'oct' && typeof jwk.k === 'string' && jwk.k.length > 0;
Prevention
- Read the nested cause after 'invalid symmetric JWK:' to branch between kty and k fixes.
- Use lowercase field names kty/k exactly as the RFC defines them.
When it happens
Trigger: importKey('jwk', { kty: 'oct' }, { name: 'AES-GCM' }) — no k field; importKey('jwk', { k: '...', kty: 'RSA' }, HMAC) — wrong kty; a JWK where the k field name is capitalized or misspelled ('K') so it unmarshals as empty.
Common situations: Minimal hand-written JWK fixtures missing the secret; field-name casing errors from manual transcription; a key-exchange endpoint returning metadata-only JWKs without secret material.
Related errors
- invalid key type: %s
- key (k) is required
- failed to parse symmetric JWK: %w
- failed to decode symmetric key: %w
- invalid EC JWK key: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/db5a753178664b17.
Report an issue: GitHub.