{"record":{"id":"6dfc230d136afb44","repo":"grafana/k6","slug":"invalid-key-type-s","errorCode":null,"errorMessage":"invalid key type: %s","messagePattern":"invalid key type: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/webcrypto/jwk.go","lineNumber":39,"sourceCode":"\n// JsonWebKey represents a JSON Web Key (JsonWebKey) key.\ntype JsonWebKey map[string]any //nolint:revive // we name this type JsonWebKey to match the spec\n\n// Set sets a key-value pair in the JWK.\nfunc (jwk *JsonWebKey) Set(key string, value any) {\n\t(*jwk)[key] = value\n}\n\n// symmetricJWK represents a symmetric JWK key.\n// It is used to unmarshal symmetric keys from JWK format.\ntype symmetricJWK struct {\n\tKty string `json:\"kty\"`\n\tK   string `json:\"k\"`\n}\n\nfunc (jwk *symmetricJWK) validate() error {\n\tif jwk.Kty != JWKOctKeyType {\n\t\treturn fmt.Errorf(\"invalid key type: %s\", jwk.Kty)\n\t}\n\n\tif jwk.K == \"\" {\n\t\treturn errors.New(\"key (k) is required\")\n\t}\n\n\treturn nil\n}\n\n// extractSymmetricJWK extracts the symmetric key from a given JWK key (JSON data).\nfunc extractSymmetricJWK(jsonKeyData []byte) ([]byte, error) {\n\tsk := symmetricJWK{}\n\tif err := json.Unmarshal(jsonKeyData, &sk); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse symmetric JWK: %w\", err)\n\t}\n\n\tif err := sk.validate(); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid symmetric JWK: %w\", err)","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/webcrypto/jwk.go#L21-L57","documentation":"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').","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst jwk = jwks.keys[0]; // happens to be kty:'EC'\nawait crypto.subtle.importKey('jwk', jwk, { name: 'AES-GCM' }, false, ['encrypt']);\n\n// after\nconst jwk = jwks.keys.find((k) => k.kty === 'oct');\nawait crypto.subtle.importKey('jwk', jwk, { name: 'AES-GCM' }, false, ['encrypt']);","handlingStrategy":"validation","validationCode":"function assertSymmetricJwk(jwk) {\n  if (jwk.kty !== 'oct') throw new Error(`expected kty 'oct' for symmetric import, got '${jwk.kty}'`);\n}","typeGuard":"const isOctJwk = (jwk) => jwk != null && typeof jwk === 'object' && jwk.kty === 'oct';","tryCatchPattern":null,"preventionTips":["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)."],"tags":["webcrypto","jwk","import-key","validation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}