{"record":{"id":"d9b87ee1a7675fb4","repo":"grafana/k6","slug":"metrics-push-url-is-required","errorCode":null,"errorMessage":"metrics push URL is required","messagePattern":"metrics push URL is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"output/cloud/expv2/metrics_client.go","lineNumber":55,"sourceCode":"// A versioned client would be better but it would require a breaking\n// change, and other services (e.g. k6-operator) depend on it, so we want\n// to stabilize the API first.\nfunc deriveMetricsURL(baseURL, testRunID string) (string, error) {\n\tif !strings.HasSuffix(baseURL, \"/v1\") {\n\t\treturn \"\", errors.New(\"a /v1 suffix is expected in the Cloud service's BaseURL path\")\n\t}\n\tif testRunID == \"\" {\n\t\treturn \"\", errors.New(\"TestRunID of the test is required\")\n\t}\n\treturn strings.TrimSuffix(baseURL, \"/v1\") + \"/v2/metrics/\" + testRunID, nil\n}\n\n// newMetricsClientWithURL builds a metricsClient with an explicit push URL.\n// It is the single metricsClient constructor: callers that need the\n// host-derived URL compute it via deriveMetricsURL first.\nfunc newMetricsClientWithURL(c metricsHTTPClient, url string) (*metricsClient, error) {\n\tif url == \"\" {\n\t\treturn nil, errors.New(\"metrics push URL is required\")\n\t}\n\treturn &metricsClient{\n\t\thttpClient: c,\n\t\turl:        url,\n\t}, nil\n}\n\n// Push the provided metrics for the given test run ID. The context cancels the\n// underlying HTTP request so a stuck push cannot block k6 shutdown.\nfunc (mc *metricsClient) push(ctx context.Context, samples *pbcloud.MetricSet) error {\n\tb, err := newRequestBody(samples)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treq, err := http.NewRequestWithContext(\n\t\tctx, http.MethodPost, mc.url, io.NopCloser(bytes.NewReader(b)))\n\tif err != nil {","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/output/cloud/expv2/metrics_client.go#L37-L73","documentation":"The v2 cloud metrics output (output/cloud/expv2) pushes protobuf samples over HTTP through a metricsClient that must be built with an explicit push URL (newMetricsClientWithURL, output/cloud/expv2/metrics_client.go:53-56). This error means that by the time the output was constructed, no metrics ingestion URL existed: neither the provisioning backend's runtime_config.metrics.push_url nor the K6_CLOUD_METRICS_PUSH_URL environment variable supplied one (internal/cmd/outputs_cloud.go:204-225 wires these). Without a URL the output cannot send any samples, so construction fails fast rather than silently dropping metrics.","triggerScenarios":"Running 'k6 cloud run --local-execution' where the orchestrator did not export K6_CLOUD_METRICS_PUSH_URL (and K6_CLOUD_TEST_RUN_TOKEN); a provisioning backend that returns a runtime_config with an empty metrics.push_url; or an embedder calling newMetricsClientWithURL directly with an empty string (metrics_client_test.go:90 exercises exactly this).","commonSituations":"CI pipelines or the k6-operator provisioning the run themselves but forgetting the scoped push credentials; an old or non-standard K6_CLOUD_HOST whose backend does not return the v2 runtime_config; typo'd env var name so the value never reaches k6.","solutions":["If an orchestration service provisions the run itself, export both K6_CLOUD_METRICS_PUSH_URL and K6_CLOUD_TEST_RUN_TOKEN (k6 errors early if only one is set - internal/cmd/outputs_cloud.go:222-225)","Verify the start_local_execution response actually contains runtime_config.metrics.push_url; if your backend returns it empty, fix the backend or supply the env override","For the legacy '-o cloud' path, ensure the cloud BaseURL ends with /v1 and a reference ID exists so deriveMetricsURL can build the URL (output/cloud/expv2/metrics_client.go:40-48)","Update k6 - older builds had different push-URL plumbing between provisioning and the expv2 output"],"exampleFix":"# before (orchestrator-managed run, no credentials exported)\nk6 cloud run --local-execution script.js\n\n# after\nexport K6_CLOUD_METRICS_PUSH_URL='https://ingest.grafana.net/v2/metrics/1234567'\nexport K6_CLOUD_TEST_RUN_TOKEN='eyJ...'\nk6 cloud run --local-execution script.js","handlingStrategy":"validation","validationCode":"# before launching an orchestrator-managed local-execution run\nif [ -z \"$K6_CLOUD_METRICS_PUSH_URL\" ] || [ -z \"$K6_CLOUD_TEST_RUN_TOKEN\" ]; then\n  echo 'missing push credentials: set K6_CLOUD_METRICS_PUSH_URL and K6_CLOUD_TEST_RUN_TOKEN' >&2\n  exit 1\nfi\nk6 cloud run --local-execution script.js","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always export K6_CLOUD_METRICS_PUSH_URL and K6_CLOUD_TEST_RUN_TOKEN as a pair; k6 fails fast when only one is present","Smoke-test custom provisioning backends by asserting runtime_config.metrics.push_url is non-empty in the response","Pin and update k6 deliberately so provisioning and expv2 output stay version-aligned"],"tags":["cloud","configuration","metrics","local-execution","env"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}