grafana/k6 · error
invalid key type: %s
Error message
invalid key type: %s
What it means
Thrown by k6 WebCrypto when importing a symmetric key from JWK format whose kty field is not 'oct'. symmetricJWK.validate (internal/js/modules/k6/webcrypto/jwk.go:37-40) enforces kty === JWKOctKeyType ('oct'); this path is taken by importKey('jwk', ...) for symmetric algorithms (HMAC, AES-GCM/CBC/KW). The error prints the actual kty so you can see what was sent (e.g. 'EC', 'RSA').
Source
Thrown at internal/js/modules/k6/webcrypto/jwk.go:39
// JsonWebKey represents a JSON Web Key (JsonWebKey) key.
type JsonWebKey map[string]any //nolint:revive // we name this type JsonWebKey to match the spec
// 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)View on GitHub (pinned to 93accf6570)
Solutions
- Match the key type to the algorithm: AES/HMAC algorithms need kty:'oct'; use ECDSA/ECDH algorithms for kty:'EC' and RSA algorithms for kty:'RSA'.
- When selecting from a JWKS, filter explicitly: keys.find((k) => k.kty === 'oct').
- Check the printed kty in the message against what your key file actually contains.
Example fix
// before
const jwk = jwks.keys[0]; // happens to be kty:'EC'
await crypto.subtle.importKey('jwk', jwk, { name: 'AES-GCM' }, false, ['encrypt']);
// after
const jwk = jwks.keys.find((k) => k.kty === 'oct');
await crypto.subtle.importKey('jwk', jwk, { name: 'AES-GCM' }, false, ['encrypt']); Defensive patterns
Strategy: validation
Validate before calling
function assertSymmetricJwk(jwk) {
if (jwk.kty !== 'oct') throw new Error(`expected kty 'oct' for symmetric import, got '${jwk.kty}'`);
} Type guard
const isOctJwk = (jwk) => jwk != null && typeof jwk === 'object' && jwk.kty === 'oct';
Prevention
- Select keys from a JWKS with .find((k) => k.kty === 'oct') for AES/HMAC.
- Keep one key type per config file and name files accordingly (aes.json, ec.json).
When it happens
Trigger: crypto.subtle.importKey('jwk', ecOrRsaJwk, { name: 'AES-GCM' }, ...) — reusing an asymmetric JWK where a symmetric one is required; kty misspelled ('OCT', 'Oct'); a JWK document selected from a JWKS by 'use' instead of 'kty' and picking the wrong entry.
Common situations: Loading keys from a JSON Web Key Set and grabbing the first key rather than the oct key; environment mismatch where a test fixture has an EC key but production uses a shared AES secret; confusion between HMAC secret (oct) and RSA signing keys in the same config file.
Related errors
- invalid symmetric JWK: %w
- 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/6dfc230d136afb44.
Report an issue: GitHub.