{"record":{"id":"ad409226c652d004","repo":"grafana/k6","slug":"both-k6-cloud-metrics-push-url-and-k6-cloud-test-r","errorCode":null,"errorMessage":"both K6_CLOUD_METRICS_PUSH_URL and K6_CLOUD_TEST_RUN_TOKEN must be set together","messagePattern":"both K6_CLOUD_METRICS_PUSH_URL and K6_CLOUD_TEST_RUN_TOKEN must be set together","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/outputs_cloud.go","lineNumber":225,"sourceCode":"// values the self-provision flow obtains from the provisioning response.\n// It requires both-or-neither and, when both are present, sets them on conf\n// (so downstream consumers such as the log pusher see them) and bakes them\n// into the serialized cloud config that the Output reads. It also reads the\n// K6_CLOUD_LOGS_* log-push config, which the external flow supplies the same\n// way.\nfunc applyExternalProvisioningCreds(\n\tgs *state.GlobalState, test *loadedAndConfiguredTest, conf *cloudapi.Config,\n) error {\n\t// The log-push config is env-supplied for an external run and likewise not\n\t// env-bound on the Config, so read it explicitly here (before the checks\n\t// below, which require the token when a logs URL is set).\n\tif err := applyExternalLogsConfig(gs, conf); err != nil {\n\t\treturn err\n\t}\n\tpushURL := gs.Env[\"K6_CLOUD_METRICS_PUSH_URL\"]\n\ttoken := gs.Env[\"K6_CLOUD_TEST_RUN_TOKEN\"]\n\tif (pushURL == \"\") != (token == \"\") {\n\t\treturn errors.New(\"both K6_CLOUD_METRICS_PUSH_URL and \" +\n\t\t\t\"K6_CLOUD_TEST_RUN_TOKEN must be set together\")\n\t}\n\t// A logs push URL is authenticated with the same scoped token, so reject a\n\t// logs URL supplied without it rather than silently streaming nothing.\n\tif token == \"\" && conf.LogsPushURL.Valid && conf.LogsPushURL.String != \"\" {\n\t\treturn errors.New(\"K6_CLOUD_LOGS_PUSH_URL requires K6_CLOUD_TEST_RUN_TOKEN\")\n\t}\n\tif pushURL == \"\" {\n\t\treturn nil\n\t}\n\n\tconf.MetricsPushURL = null.StringFrom(pushURL)\n\tconf.TestRunToken = null.StringFrom(token)\n\traw, err := cloudConfToRawMessage(*conf)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not serialize cloud configuration: %w\", err)\n\t}\n\tif test.derivedConfig.Collectors == nil {","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/cmd/outputs_cloud.go#L207-L243","documentation":"Thrown by applyExternalProvisioningCreds when exactly one of K6_CLOUD_METRICS_PUSH_URL and K6_CLOUD_TEST_RUN_TOKEN is set (the `(pushURL == \"\") != (token == \"\")` XOR check). An externally-provisioned cloud run needs both the metrics endpoint and the scoped token that authenticates to it.","triggerScenarios":"Launching k6 with a PushRefID-based external run where K6_CLOUD_METRICS_PUSH_URL is exported but K6_CLOUD_TEST_RUN_TOKEN is missing (or the reverse).","commonSituations":"CI secret masking/redaction stripping the token env var; copying only half of a Grafana Cloud 'external run' command; push URL set globally in CI while the token is passed per-job (or vice versa).","solutions":["Export both variables together, exactly as emitted by the cloud UI/CLI (they are generated as a pair)","If an external run was not intended, unset both and authenticate with K6_CLOUD_TOKEN instead","Verify the CI secret manager did not redact, truncate, or rename K6_CLOUD_TEST_RUN_TOKEN"],"exampleFix":"# before\nexport K6_CLOUD_METRICS_PUSH_URL=https://.../api/v1/write\nk6 run --out cloud script.js\n\n# after\nexport K6_CLOUD_METRICS_PUSH_URL=https://.../api/v1/write\nexport K6_CLOUD_TEST_RUN_TOKEN=eyJ...\nk6 run --out cloud script.js","handlingStrategy":"validation","validationCode":"# Both or neither\nif [ -n \"$K6_CLOUD_METRICS_PUSH_URL\" ] && [ -z \"$K6_CLOUD_TEST_RUN_TOKEN\" ] || [ -z \"$K6_CLOUD_METRICS_PUSH_URL\" ] && [ -n \"$K6_CLOUD_TEST_RUN_TOKEN\" ]; then\n  echo \"K6_CLOUD_METRICS_PUSH_URL and K6_CLOUD_TEST_RUN_TOKEN must be set together\" >&2; exit 1;\nfi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Export external-provisioning env vars as one block from the cloud-provided command","Name CI secrets exactly K6_CLOUD_TEST_RUN_TOKEN and verify masking does not empty them","Add a startup env check in wrapper scripts so the failure is caught before the test starts"],"tags":["cloud","environment","configuration","auth"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}