{"record":{"id":"952960bd1d437ec5","repo":"grafana/k6","slug":"a-v1-suffix-is-expected-in-the-cloud-service-s-ba","errorCode":null,"errorMessage":"a /v1 suffix is expected in the Cloud service's BaseURL path","messagePattern":"a /v1 suffix is expected in the Cloud service's BaseURL path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"output/cloud/expv2/metrics_client.go","lineNumber":42,"sourceCode":"// the collected metrics from the Cloud output\n// to the remote service.\ntype metricsClient struct {\n\thttpClient metricsHTTPClient\n\turl        string\n}\n\n// deriveMetricsURL builds the v2 metrics-ingestion URL from the Cloud\n// service base URL, used when no explicit push URL was provided.\n//\n// The cloudapi.Client works across different versions of the API: test\n// lifecycle management is under /v1 while metrics ingestion is /v2.\n// The client has /v1 hard-coded, so we trim it and replace with /v2.\n// 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","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/output/cloud/expv2/metrics_client.go#L24-L60","documentation":"Returned by deriveMetricsURL (output/cloud/expv2/metrics_client.go:41-43), which builds the v2 metrics-ingestion URL from the Cloud service base URL. The cloudapi.Client hard-codes /v1 for test lifecycle management, so the expv2 output requires the base URL to end in /v1 and derives ingestion as <trimmed>/v2/metrics/<testRunID> (line 47). A base URL without the /v1 suffix means the API-version convention was violated and URL derivation would be wrong. Reached from Output.Start (output.go:134) when neither metricsHTTPClient nor metricsURL was injected; the error surfaces wrapped as 'failed to derive the metrics push URL'.","triggerScenarios":"Setting K6_CLOUD_HOST=https://api.cloud.k6.io (no /v1), or a custom/proxy Cloud host like https://cloud.internal.example. Defaults such as https://cloudutils.grafana.com/v1 and https://api.k6.io/v1 already carry the suffix.","commonSituations":"Pointing k6 at Grafana Cloud or a self-hosted/proxied Cloud endpoint and forgetting the /v1 path; env var typos; on-prem gateway URLs rewritten to strip path prefixes.","solutions":["Append /v1 to the host, e.g. K6_CLOUD_HOST=https://cloudutils.grafana.com/v1","If the endpoint genuinely has a different versioning scheme, bypass derivation by providing an explicit push URL: set both K6_CLOUD_METRICS_PUSH_URL and K6_CLOUD_TEST_RUN_TOKEN (output.go requires them together)"],"exampleFix":"# before\nK6_CLOUD_HOST=https://cloudutils.grafana.com k6 cloud script.js\n\n# after\nK6_CLOUD_HOST=https://cloudutils.grafana.com/v1 k6 cloud script.js","handlingStrategy":"validation","validationCode":"# shell: assert the /v1 suffix before running\nH=\"${K6_CLOUD_HOST:-https://cloudutils.grafana.com/v1}\"\ncase \"$H\" in */v1) ;; *) echo \"K6_CLOUD_HOST must end with /v1\"; exit 1;; esac\nexport K6_CLOUD_HOST=\"$H\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include the /v1 path in K6_CLOUD_HOST (e.g. https://cloudutils.grafana.com/v1, https://api.k6.io/v1)","If your gateway cannot keep /v1, bypass derivation with K6_CLOUD_METRICS_PUSH_URL plus K6_CLOUD_TEST_RUN_TOKEN (they must be set together)"],"tags":["cloud-output","baseurl","config","k6"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}