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

  1. 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'.
  2. When selecting from a JWKS, filter explicitly: keys.find((k) => k.kty === 'oct').
  3. 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

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


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/6dfc230d136afb44. Report an issue: GitHub.