grafana/k6 · error

invalid tls value: '%#v', expected (optional) keys: cert, ke

Error message

invalid tls value: '%#v', expected (optional) keys: cert, key, password, and cacerts

What it means

Thrown by k6's gRPC Client.connect() when the tls connect option is not an object. parseConnectTLSParam (internal/js/modules/k6/grpc/params.go:220-226) requires the value of the 'tls' key to be a map (JS object) whose optional keys are cert, key, password, cacerts; anything else — a string, boolean, number, or array — fails the type assertion v.(map[string]any) and produces this error.

Source

Thrown at internal/js/modules/k6/grpc/params.go:225

			var ok bool
			result.Authority, ok = v.(string)
			if !ok {
				return result, fmt.Errorf("invalid authority value: '%#v', it needs to be a string", v)
			}
		default:
			return result, fmt.Errorf("unknown connect param: %q", k)
		}
	}

	return result, nil
}

func parseConnectTLSParam(params *connectParams, v any) error {
	var ok bool
	params.TLS, ok = v.(map[string]any)

	if !ok {
		return fmt.Errorf("invalid tls value: '%#v', expected (optional) keys: cert, key, password, and cacerts", v)
	}
	// optional map keys below
	if cert, certok := params.TLS["cert"]; certok {
		if _, ok = cert.(string); !ok {
			return fmt.Errorf("invalid tls cert value: '%#v', it needs to be a PEM formatted string", v)
		}
	}
	if key, keyok := params.TLS["key"]; keyok {
		if _, ok = key.(string); !ok {
			return fmt.Errorf("invalid tls key value: '%#v', it needs to be a PEM formatted string", v)
		}
	}
	if pass, passok := params.TLS["password"]; passok {
		if _, ok = pass.(string); !ok {
			return fmt.Errorf("invalid tls password value: '%#v', it needs to be a string", v)
		}
	}
	if cacerts, cacertsok := params.TLS["cacerts"]; cacertsok {

View on GitHub (pinned to 93accf6570)

Solutions

  1. Make the tls option an object literal: tls: { ... } with only the keys cert, key, password, cacerts.
  2. If you only want the system CAs, pass tls: {} (empty object) or omit the tls option entirely.
  3. To disable TLS, use plaintext: true instead of tls: false.
  4. Check the printed %#v value in the message to see what type was actually passed.

Example fix

// before
client.connect('host:443', { tls: true });

// after
client.connect('host:443', { plaintext: false, tls: {} });
Defensive patterns

Strategy: validation

Validate before calling

function validateTlsParam(params = {}) {
  if (!('tls' in params)) return;
  if (typeof params.tls !== 'object' || params.tls === null || Array.isArray(params.tls)) {
    throw new Error('connect param tls must be an object with optional keys cert, key, password, cacerts');
  }
}

Type guard

const isTlsObject = (v) => v === undefined || (typeof v === 'object' && v !== null && !Array.isArray(v));

Try / catch

try { client.connect(addr, { tls }); } catch (e) { if (/invalid tls value/.test(e.message)) { /* fall back to default TLS: client.connect(addr, {}) */ } throw e; }

Prevention

When it happens

Trigger: Passing tls: true (a common mistake from older k6 versions where tls was a boolean), tls: 'cert.pem', tls: [cert], or tls: null exported to a non-object. Note the message prints the whole offending value with %#v, so you see exactly what was received.

Common situations: Scripts written for k6 <0.42-style APIs where TLS was toggled differently; passing a file path string instead of the file contents object; double-wrapping the tls object ({ tls: { tls: {...} } }).

Understand the failure class

Related errors


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