{"record":{"id":"87065c34cc9e7610","repo":"grafana/k6","slug":"received-empty-projects-page-with-next-link","errorCode":null,"errorMessage":"received empty projects page with next link","messagePattern":"received empty projects page with next link","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloudapi/v6/api.go","lineNumber":76,"sourceCode":"\t\tres, err := c.listProjectsPage(ctx, skip, pageSize)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, project := range res.Value {\n\t\t\tprojects = append(projects, Project{\n\t\t\t\tID:        project.Id,\n\t\t\t\tName:      project.Name,\n\t\t\t\tIsDefault: project.IsDefault,\n\t\t\t})\n\t\t}\n\n\t\tif res.NextLink == nil || *res.NextLink == \"\" {\n\t\t\treturn projects, nil\n\t\t}\n\n\t\tif len(res.Value) == 0 {\n\t\t\treturn nil, errors.New(\"received empty projects page with next link\")\n\t\t}\n\t\tskip += pageSize\n\t}\n}\n\nfunc (c *Client) listProjectsPage(\n\tctx context.Context, skip, top int32,\n) (*k6cloud.ProjectListResponse, error) {\n\tres, hr, err := c.apiClient.ProjectsAPI.\n\t\tProjectsList(c.authCtx(ctx)).\n\t\tXStackId(c.stackID).\n\t\tSkip(skip).\n\t\tTop(top).\n\t\tExecute()\n\tdefer closeResponse(hr, &err)\n\n\tif err := CheckResponse(hr, err); err != nil {\n\t\treturn nil, err","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/cloudapi/v6/api.go#L58-L94","documentation":"Thrown by the k6 Cloud v6 API client's project-list pagination loop when a page returned by ProjectsList contains a non-empty nextLink but an empty value array. The client treats this as a violated pagination contract: skipping would silently drop projects and following the link blindly could loop forever, so it aborts the whole listing.","triggerScenarios":"Calling Client.ListProjects (project discovery during `k6 cloud login` or project resolution) where the server responds 200 OK with Value=[] while NextLink is set. Only the server/proxy response shape triggers it; client-side config cannot.","commonSituations":"A Grafana Cloud API pagination glitch, an intermediate proxy or gateway mangling the response body, or a version skew between the vendored k6-cloud-openapi-client-go and the live API. Rare; usually transient.","solutions":["Retry the same command once or twice; most occurrences are transient server-side","Verify the stack URL/slug is correct so you are hitting the expected API endpoint","Upgrade k6 to the latest release in case the vendored cloud API client is out of sync with the API","Re-run with K6_LOG_LEVEL=debug (and HTTP debugging enabled) to capture the raw page response and report it to Grafana support"],"exampleFix":"// before: single-shot call that surfaces the pagination anomaly directly\nprojects, err := client.ListProjects(ctx)\nif err != nil {\n    return err // \"received empty projects page with next link\"\n}\n\n// after: retry the listing a few times with backoff\nvar projects []k6cloud.Project\nfor attempt := 0; attempt < 3; attempt++ {\n    projects, err = client.ListProjects(ctx)\n    if err == nil {\n        break\n    }\n    time.Sleep(time.Duration(attempt+1) * time.Second)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"var projects []k6cloud.Project\nerr := backoff.Retry(func() error {\n    var e error\n    projects, e = client.ListProjects(ctx)\n    if e != nil && strings.Contains(e.Error(), \"empty projects page\") {\n        return e // transient pagination anomaly, retry\n    }\n    return backoff.Permanent(e)\n}, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 3))","preventionTips":["Pin a recent k6 version so the vendored cloud API client tracks the deployed API","In smoke tests against the cloud API, exercise ListProjects once before long runs to fail early","Enable debug HTTP logging (K6_LOG_LEVEL=debug) when first integrating to capture anomalous pages"],"tags":["cloud","api","pagination","go","server-anomaly"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}