grafana/k6 · error

unsupported exporter protocol

Error message

unsupported exporter protocol %s

What it means

The OTel metrics exporter builder dispatches on the configured protocol and only knows 'grpc' and 'http/protobuf' (k6's grpcExporterProtocol/httpExporterProtocol constants). Any other value reaches the default branch and fails at exporter construction. Note that the bare value 'http' is invalid — the HTTP protocol's full name is 'http/protobuf' — and even an empty string can reach here because Validate() skips its protocol check when the value is empty.

Solutions

  1. Use exactly one of: grpc or http/protobuf
  2. Check for an empty K6_OTEL_EXPORTER_PROTOCOL override and remove it so the grpc default applies
  3. If you validate configs yourself, enforce the two allowed values before k6 constructs the exporter

Example fix

# before
export K6_OTEL_EXPORTER_PROTOCOL=http
k6 run -o opentelemetry script.js
# after
export K6_OTEL_EXPORTER_PROTOCOL=http/protobuf
k6 run -o opentelemetry script.js
Defensive patterns

Strategy: validation

Validate before calling

// Shell: allow-list the protocol
p="${K6_OTEL_EXPORTER_PROTOCOL:-grpc}"
if [ "$p" != "grpc" ] && [ "$p" != "http/protobuf" ]; then
  echo "K6_OTEL_EXPORTER_PROTOCOL must be grpc or http/protobuf" >&2; exit 1
fi

Type guard

function isSupportedOtelProtocol(p) {
  return p === 'grpc' || p === 'http/protobuf';
}

Prevention

When it happens

Trigger: K6_OTEL_EXPORTER_PROTOCOL=http (should be http/protobuf); proto=http/json (unsupported); an empty-string protocol override slipping past config validation; programmatic Config construction that skips GetConsolidatedConfig/Validate.

Common situations: Using OTel SDK spellings ('http', 'http/json') that k6 does not accept; empty env var overrides; wrappers building Config directly.

Related errors


AI-assisted analysis of grafana/k6@01ffac6f24 (2026-08-18). Data as JSON: /api/errors/7a607d2caee9c0e1. Report an issue: GitHub.

Appendix: source

Thrown at internal/output/opentelemetry/exporter.go:54

		headers, err = parseHeaders(cfg.Headers.String)
		if err != nil {
			return nil, fmt.Errorf("failed to parse headers: %w", err)
		}
	}

	// if at least valid user was configured, use basic auth
	if cfg.HTTPUsername.Valid {
		auth := []byte(cfg.HTTPUsername.String + ":" + cfg.HTTPPassword.String)
		headers["Authorization"] = fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString(auth))
	}

	switch cfg.ExporterProtocol.String {
	case grpcExporterProtocol:
		return buildGRPCExporter(ctx, cfg, tlsConfig, headers)
	case httpExporterProtocol:
		return buildHTTPExporter(ctx, cfg, tlsConfig, headers)
	default:
		return nil, errors.New("unsupported exporter protocol " + cfg.ExporterProtocol.String)
	}
}

func buildHTTPExporter(
	ctx context.Context,
	cfg Config,
	tlsConfig *tls.Config,
	headers map[string]string,
) (metric.Exporter, error) {
	opts := []otlpmetrichttp.Option{
		otlpmetrichttp.WithEndpoint(cfg.HTTPExporterEndpoint.String),
		otlpmetrichttp.WithURLPath(cfg.HTTPExporterURLPath.String),
	}

	if cfg.HTTPExporterInsecure.Bool {
		opts = append(opts, otlpmetrichttp.WithInsecure())
	}

View on GitHub (pinned to 01ffac6f24)