{"record":{"id":"fb94c163dad22762","repo":"grafana/k6","slug":"token-is-required-to-create-cloud-api-client","errorCode":null,"errorMessage":"token is required to create cloud API client","messagePattern":"token is required to create cloud API client","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloudapi/v6/client.go","lineNumber":32,"sourceCode":"\t\"go.k6.io/k6/v2/errext\"\n\t\"go.k6.io/k6/v2/internal/cloudapi/clientcfg\"\n\t\"go.k6.io/k6/v2/internal/cloudapi/httperr\"\n)\n\n// Client handles communication with the k6 Cloud API.\ntype Client struct {\n\tapiClient *k6cloud.APIClient\n\ttoken     string\n\tstackID   int32\n\tbaseURL   string\n\n\tlogger logrus.FieldLogger\n}\n\n// NewClient return a new client for the cloud API\nfunc NewClient(logger logrus.FieldLogger, token, host, version string, timeout time.Duration) (*Client, error) {\n\tif token == \"\" {\n\t\treturn nil, fmt.Errorf(\"token is required to create cloud API client\")\n\t}\n\n\tcfg := clientcfg.New(host, version, \"Global k6 Cloud API.\", timeout)\n\n\tc := &Client{\n\t\tapiClient: k6cloud.NewAPIClient(cfg),\n\t\ttoken:     token,\n\t\tbaseURL:   fmt.Sprintf(\"%s/cloud/v6\", host),\n\t\tlogger:    logger,\n\t}\n\treturn c, nil\n}\n\n// SetStackID sets the stack ID for the client. It returns an error if\n// stackID does not fit in the int32 range the underlying SDK requires for\n// the X-Stack-Id header.\nfunc (c *Client) SetStackID(stackID int64) error {\n\tif stackID < math.MinInt32 || stackID > math.MaxInt32 {","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/grafana/k6/blob/8d06114777191d21ef885d05c5f8aa9e1ed838ae/internal/cloudapi/v6/client.go#L14-L50","documentation":"cloudapiv6.NewClient refuses to construct a client when the token argument is an empty string — there is no anonymous access to the k6 Cloud v6 API. This is a fail-fast guard at client construction (client.go:31), before any request is made, so hitting it means the caller resolved configuration without a token.","triggerScenarios":"Calling NewClient with \"\" because the token was never read from flags/env/disk config, a K6_CLOUD_TOKEN env var set to empty, or a config loader silently dropping the token field. The cmd layer normally prevents this via checkCloudLogin, so direct API users hit it most.","commonSituations":"Extensions (xk6) or scripts embedding the cloudapiv6 client without running login first, CI jobs that mask the token to an empty string, or home-dir config files (~/.) removed between login and run.","solutions":["Set the token: `k6 cloud login` (interactive) or export K6_CLOUD_TOKEN, then construct the client","If calling NewClient directly, resolve the token via the same consolidated config the CLI uses and error out to the user when missing","In CI, assert the secret is non-empty before invoking k6 (e.g. `test -n \"$K6_CLOUD_TOKEN\"`)"],"exampleFix":"// before\nclient, err := cloudapiv6.NewClient(logger, os.Getenv(\"K6_CLOUD_TOKEN\"), host, version, timeout)\n\n// after\ntoken := os.Getenv(\"K6_CLOUD_TOKEN\")\nif token == \"\" {\n    return nil, fmt.Errorf(\"set K6_CLOUD_TOKEN or run `k6 cloud login`\")\n}\nclient, err := cloudapiv6.NewClient(logger, token, host, version, timeout)","handlingStrategy":"validation","validationCode":"token := strings.TrimSpace(cfg.Token.String)\nif token == \"\" {\n    return nil, fmt.Errorf(\"K6_CLOUD_TOKEN not set; run `k6 cloud login`\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralize token resolution in one config loader that fails fast on empty","In CI, add a guard step: test -n \"$K6_CLOUD_TOKEN\" || exit 1","Never construct the v6 client before verifying the login check passes"],"tags":["auth","missing-token","cloud-api","fail-fast"],"backgroundTag":"missing-api-token","analyzedSha":"8d06114777191d21ef885d05c5f8aa9e1ed838ae","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}