{"record":{"id":"0f2c5b0f405791f1","repo":"grafana/k6","slug":"creating-v6-client-w","errorCode":null,"errorMessage":"creating v6 client: %w","messagePattern":"creating v6 client: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloudapi/provisioning/client.go","lineNumber":54,"sourceCode":"// NewClient returns a new provisioning Client. It internally constructs\n// both a k6cloud.APIClient (for provisioning endpoints) and a v6.Client\n// (for v6 operations like FetchTest). The stackID identifies the\n// Grafana Cloud stack and is required for the stack-scoped requests.\nfunc NewClient(\n\tlogger logrus.FieldLogger,\n\ttoken, host, version string,\n\tstackID int64,\n\ttimeout time.Duration,\n) (*Client, error) {\n\tif token == \"\" {\n\t\treturn nil, fmt.Errorf(\"token is required to create provisioning API client\")\n\t}\n\n\tcfg := clientcfg.New(host, version, \"k6 Cloud API (provisioning).\", timeout)\n\n\tv6c, err := v6.NewClient(logger, token, host, version, timeout)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating v6 client: %w\", err)\n\t}\n\n\tc := &Client{\n\t\tapiClient: k6cloud.NewAPIClient(cfg),\n\t\tv6Client:  v6c,\n\t\ttoken:     token,\n\t\tstackID:   stackID,\n\t\thost:      host,\n\t\tversion:   version,\n\t\tlogger:    logger,\n\t}\n\t// Propagate the stack ID to the embedded v6 client. v6.SetStackID\n\t// validates that it fits the int32 the SDK's X-Stack-Id requires —\n\t// that is the single place the int32 boundary is enforced.\n\tif err := c.v6Client.SetStackID(stackID); err != nil {\n\t\treturn nil, err\n\t}\n\t// The provisioning endpoints carry the stack ID as a decimal-string","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/cloudapi/provisioning/client.go#L36-L72","documentation":"provisioning.NewClient (internal/cloudapi/provisioning/client.go:54) wraps the failure of the embedded v6.NewClient. Today v6.NewClient only fails on an empty token, which the caller has already guarded, so this wrapper is defensive and effectively unreachable; if it ever fires, the wrapped error (%w) carries the real cause.","triggerScenarios":"v6.NewClient returning an error during provisioning client construction — only possible if the two guards diverge in a future version (e.g. v6 starts validating host or token format).","commonSituations":"Version drift after refactoring one client's constructor but not the other; effectively never seen in the wild today.","solutions":["Inspect the wrapped error with errors.Unwrap / %w chaining to find the real cause","Check the k6 version and the v6.NewClient guard in that version","If adding new validation to v6.NewClient, mirror it (or pre-validate) in provisioning.NewClient to keep the message actionable"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isV6ConstructionFailure(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"creating v6 client\")\n}","tryCatchPattern":"client, err := provisioning.NewClient(...)\nif err != nil {\n\tif inner := errors.Unwrap(err); inner != nil {\n\t\tlog.Printf(\"v6 client failure cause: %v\", inner)\n\t}\n\treturn err\n}","preventionTips":["Always unwrap chained construction errors before diagnosing","When adding validation to one constructor, mirror it in wrappers to keep messages actionable"],"tags":["client-construction","defensive","auth","error-chaining"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}