grafana/k6 · error

stack URL is required to validate token

Error message

stack URL is required to validate token

What it means

Defensive guard in Client.ValidateToken: the method needs a stack URL to send as the X-Stack-Url header when validating an API token, and it refuses to proceed when the argument is an empty string. In-tree callers (k6 cloud login) always pass a normalized non-empty URL, so this error is only seen by Go code calling the internal v6 client directly or by wiring bugs.

Solutions

  1. Pass a full stack URL such as https://my-team.grafana.net (or normalize a slug like my-team yourself before calling)
  2. If the stack is optional in your flow, resolve it from config/env first and skip validation when absent
  3. Check the variable feeding the call — an unset env var or empty flag usually produces the empty string

Example fix

// before
authResp, err := client.ValidateToken(ctx, os.Getenv("K6_STACK")) // K6_STACK unset -> error

// after
stackURL := strings.TrimSpace(os.Getenv("K6_STACK"))
if stackURL == "" {
    return errors.New("K6_STACK must be set to a stack URL or slug")
}
if !strings.Contains(stackURL, "://") {
    stackURL = fmt.Sprintf("https://%s.grafana.net", stackURL)
}
authResp, err := client.ValidateToken(ctx, stackURL)
Defensive patterns

Strategy: validation

Validate before calling

stackURL := strings.TrimSpace(stackURL)
if stackURL == "" {
    return errors.New("stack URL is required to validate token")
}
if _, err := url.Parse(stackURL); err != nil {
    return fmt.Errorf("invalid stack URL: %w", err)
}
if !strings.Contains(stackURL, "://") {
    stackURL = fmt.Sprintf("https://%s.grafana.net", stackURL)
}

Type guard

func isValidStackURL(s string) bool {
	s = strings.TrimSpace(s)
	if s == "" {
		return false
	}
	u, err := url.Parse(s)
	return err == nil && u.Host != "" && (u.Scheme == "http" || u.Scheme == "https")
}

Prevention

When it happens

Trigger: Calling cloudapiv6 Client.ValidateToken(ctx, "") (or with a whitespace/empty-after-trim string) — e.g. building the client in your own tooling and passing an unresolved stack variable. The check happens before any HTTP request is made.

Common situations: Custom automation that embeds k6's internal cloudapi package; a stack slug read from an unset environment variable; code that assumes ValidateToken will look up the stack from client config (it does not — the URL must be passed per call).

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of grafana/k6@01ffac6f24 (2026-08-18). Data as JSON: /api/errors/bd8d6ea3d31c19c2. Report an issue: GitHub.

Appendix: source

Thrown at internal/cloudapi/v6/api.go:190

	zones := make([]LoadZone, 0, len(res.Value))
	for _, zone := range res.Value {
		zones = append(zones, LoadZone{
			ID:           zone.Id,
			K6LoadZoneID: zone.K6LoadZoneId,
			Name:         zone.Name,
			Public:       zone.Public,
			Available:    zone.Available,
		})
	}

	return zones, nil
}

// ValidateToken validates the cloud authentication token.
func (c *Client) ValidateToken(ctx context.Context, stackURL string) (_ *k6cloud.AuthenticationResponse, err error) {
	if stackURL == "" {
		return nil, errors.New("stack URL is required to validate token")
	}
	if _, err := url.Parse(stackURL); err != nil {
		return nil, fmt.Errorf("invalid stack URL: %w", err)
	}

	res, hr, err := c.apiClient.AuthorizationAPI.
		Auth(c.authCtx(ctx)).
		XStackUrl(stackURL).
		Execute()
	defer closeResponse(hr, &err)

	if err := CheckResponse(hr, err); err != nil {
		return nil, err
	}
	if res == nil {
		return nil, errUnknown
	}

View on GitHub (pinned to 01ffac6f24)