grafana/k6 · error

failed to extract algorithm: %w

Error message

failed to extract algorithm: %w

What it means

Thrown by k6 WebCrypto when exporting a symmetric key to JWK and the algorithm-to-'alg' mapping fails. exportSymmetricJWK (internal/js/modules/k6/webcrypto/jwk.go:83-86) calls extractAlg to derive the alg identifier (e.g. HS256 for HMAC/SHA-256, A256GCM for 256-bit AES-GCM); the wrapped inner error is one of: hash algorithm name shorter than 4 chars, named algorithm shorter than 4 chars, or 'unsupported algorithm'. This is essentially an internal consistency check — a well-formed key generated/imported through the normal API always carries a mappable algorithm.

Source

Thrown at internal/js/modules/k6/webcrypto/jwk.go:85

// 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 returned
	exported := &JsonWebKey{}

	exported.Set("k", base64URLEncode(rawKey))
	exported.Set("kty", JWKOctKeyType)
	exported.Set("ext", key.Extractable)
	exported.Set("key_ops", key.Usages)

	algV, err := extractAlg(key.Algorithm, len(rawKey))
	if err != nil {
		return nil, fmt.Errorf("failed to extract algorithm: %w", err)
	}
	exported.Set("alg", algV)

	return exported, nil
}

func extractAlg(inAlg any, keyLen int) (string, error) {
	switch alg := inAlg.(type) {
	case hasHash:
		v := alg.hash()
		if len(v) < 4 {
			return "", errors.New("length of hash algorithm is less than 4: " + v)
		}
		return "HS" + v[4:], nil
	case hasAlg:
		v := alg.alg()
		if len(v) < 4 {
			return "", errors.New("length of named algorithm is less than 4: " + v)

View on GitHub (pinned to 93accf6570)

Solutions

  1. As a workaround, export symmetric keys as 'raw' (base64url the bytes yourself) instead of 'jwk'.
  2. Recreate the key through the standard path — generateKey or importKey('raw'|'jwk') with a canonical algorithm name — then export 'jwk' again.
  3. If it reproduces with a normally-created key, file a k6 issue with the algorithm dictionary used.

Example fix

// before
const jwk = await crypto.subtle.exportKey('jwk', key); // failed to extract algorithm

// after
const raw = await crypto.subtle.exportKey('raw', key);
const jwk = { kty: 'oct', k: bytesToBase64url(new Uint8Array(raw)) };
Defensive patterns

Strategy: fallback

Try / catch

let exported;
try {
  exported = await crypto.subtle.exportKey('jwk', key);
} catch (e) {
  if (/failed to extract algorithm/.test(e.message)) {
    const raw = await crypto.subtle.exportKey('raw', key);
    exported = { kty: 'oct', k: bytesToBase64url(new Uint8Array(raw)) };
  } else throw e;
}

Prevention

When it happens

Trigger: crypto.subtle.exportKey('jwk', symmetricKey) where the key's algorithm object was constructed through an unusual path — e.g. an imported raw key whose algorithm identifier got a truncated/nonstandard name, or a CryptoKey obtained from a nonstandard extension. Keys created by k6's own generateKey/importKey do not hit it.

Common situations: Interoperating with scripts that pass algorithm objects they built by hand (deep-cloning and mutating algorithm dictionaries); k6 version skew where a custom build changed algorithm names; extremely rare in vanilla k6 usage.

Related errors


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