grafana/k6 · error

failed to load TLS credentials from file: %w

Error message

failed to load TLS credentials from file: %w

What it means

When the insights gRPC client is configured with a TLS cert file (TLSConfig.CertFile), dial options construction calls credentials.NewClientTLSFromFile (internal/cloudapi/insights/client.go:225-229). Any failure reading or parsing that file - missing path, permission denied, content that is not a PEM certificate bundle - returns this wrapped error and Dial aborts.

Source

Thrown at internal/cloudapi/insights/client.go:228

	if cfg.ConnectConfig.Block {
		opts = append(opts, grpc.WithBlock()) //nolint:staticcheck
	}

	if cfg.ConnectConfig.FailOnNonTempDialError {
		opts = append(opts, grpc.FailOnNonTempDialError(true)) //nolint:staticcheck
	}

	if cfg.ConnectConfig.Dialer != nil {
		opts = append(opts, grpc.WithContextDialer(cfg.ConnectConfig.Dialer))
	}

	if cfg.TLSConfig.Insecure { //nolint: nestif
		opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
	} else {
		if cfg.TLSConfig.CertFile != "" {
			creds, err := credentials.NewClientTLSFromFile(cfg.TLSConfig.CertFile, "")
			if err != nil {
				return nil, fmt.Errorf("failed to load TLS credentials from file: %w", err)
			}
			opts = append(opts, grpc.WithTransportCredentials(creds))
		} else {
			opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{MinVersion: tls.VersionTLS13})))
		}
	}

	if cfg.AuthConfig.Enabled {
		opts = append(opts, grpc.WithPerRPCCredentials(newPerRPCCredentials(cfg.AuthConfig)))
	}

	rI, err := retryInterceptor(cfg.RetryConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to create retry interceptors: %w", err)
	}

	opts = append(opts, grpc.WithChainUnaryInterceptor([]grpc.UnaryClientInterceptor{rI}...))

View on GitHub (pinned to 93accf6570)

Solutions

  1. Verify the exact path exists and is readable from where k6 runs: 'ls -l /path/ca.pem' inside the same container
  2. Validate the file is a PEM certificate: 'openssl x509 -in /path/ca.pem -text -noout' (and check the bundle contains certs)
  3. Use an absolute path in the configuration to avoid working-directory surprises
  4. Re-mount or re-copy the secret if it is empty/corrupt

Example fix

# before - path valid on the host, not in the container
volumeMounts: [{ name: ca, mountPath: /etc/ca }]   # file lands at /etc/ca/ca.pem
config: certFile: /etc/ca.pem   # wrong

# after
config: certFile: /etc/ca/ca.pem
Defensive patterns

Strategy: validation

Validate before calling

# deployment pre-flight for the insights CA file
CERT=/etc/k6/insights-ca.pem
[ -s "$CERT" ] || { echo "cert file missing/empty: $CERT"; exit 1; }
openssl x509 -in "$CERT" -noout >/dev/null || { echo "not a valid PEM cert: $CERT"; exit 1; }

Prevention

When it happens

Trigger: CertFile set to a path that does not exist in k6's container/filesystem; a mounted secret that is empty or corrupt; a file containing keys but no certificates; relative path resolved against an unexpected working directory.

Common situations: Kubernetes secret mounted at a different path than configured; CI copying the CA with the wrong name; PEM bundle truncated in transit; permissions blocking read.

Understand the failure class

Related errors


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