{"record":{"id":"26211ddb113868f4","repo":"grafana/k6","slug":"fetching-test-status-w","errorCode":null,"errorMessage":"fetching test status: %w","messagePattern":"fetching test status: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloudapi/provisioning/api.go","lineNumber":144,"sourceCode":"\n// WaitForTestRunReady polls GET /cloud/v6/test_runs/{id} until the\n// backend status becomes \"initializing\" (signalling that k6 can begin\n// local execution). Returns an error if the test run reaches \"aborted\"\n// or \"completed\" first. Cancellable via context. Logs status transitions\n// at Debug level once per change.\n//\n// If pollInterval is <= 0 the defaultWaitPollInterval is used.\nfunc (c *Client) WaitForTestRunReady(ctx context.Context, testRunID int64, pollInterval time.Duration) error {\n\tif pollInterval <= 0 {\n\t\tpollInterval = defaultWaitPollInterval\n\t}\n\n\tvar lastStatus string\n\n\tfor {\n\t\tprogress, err := c.v6Client.FetchTest(ctx, testRunID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"fetching test status: %w\", err)\n\t\t}\n\n\t\tstatus := progress.Status.String()\n\n\t\tswitch v6.Status(status) {\n\t\tcase v6.StatusInitializing:\n\t\t\treturn nil\n\n\t\tcase v6.StatusAborted:\n\t\t\tvar abortMsg string\n\t\t\tfor _, e := range progress.StatusHistory {\n\t\t\t\tif e.Status == v6.StatusAborted && e.Message != \"\" {\n\t\t\t\t\tabortMsg = e.Message\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif abortMsg != \"\" {\n\t\t\t\treturn fmt.Errorf(\"test run aborted before starting: %s\", abortMsg)","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/grafana/k6/blob/01ffac6f245854c1b8adc6a69857c76a15f90022/internal/cloudapi/provisioning/api.go#L126-L162","documentation":"Returned by Client.WaitForTestRunReady (internal/cloudapi/provisioning/api.go:144) when the underlying v6Client.FetchTest call fails while polling GET /cloud/v6/test_runs/{id}. A single failed poll aborts the whole wait — there is no retry around FetchTest at this layer — so even one transient failure (network blip, 401, 5xx) ends the wait-for-ready phase and the run stops before starting.","triggerScenarios":"Any FetchTest error inside the polling loop: expired/revoked API token mid-wait, test run deleted in the UI (404), network interruption, cloud API returning 4xx/5xx, or the parent context being cancelled (surfacing as ctx error inside the HTTP call).","commonSituations":"Long queue times on k6 Cloud with flaky connectivity; token permissions changed or token revoked while the test sat in queue; polling a test run someone deleted; strict egress firewalls dropping long-lived idle sessions.","solutions":["Inspect the wrapped error: 401/403 => token problem, 404 => test run gone, transport errors => network, context.Canceled => the run was interrupted locally.","Re-run the test; if the queue wait is long and the network is flaky, that alone can trigger it.","Verify the token still has access to the load test / project in the Grafana Cloud k6 UI.","If you control the caller, wrap WaitForTestRunReady with a bounded retry loop so single-poll failures don't kill the run.","Check status.k6.io / Grafana Cloud status page for API incidents during the wait window."],"exampleFix":"// before\nif err := c.WaitForTestRunReady(ctx, testRunID, pollInterval); err != nil { return err }\n\n// after — tolerate transient poll failures without masking terminal ones\nvar err error\nfor attempt := 0; attempt < 3; attempt++ {\n\terr = c.WaitForTestRunReady(ctx, testRunID, pollInterval)\n\tif err == nil { break }\n\tvar apiErr *cloudapi.APIError // adapt to the v6 client's typed error\n\tif errors.As(err, &apiErr) && (apiErr.Code == 404 || apiErr.Code == 401) {\n\t\tbreak // terminal — retrying cannot help\n\t}\n\ttime.Sleep(time.Duration(attempt+1) * 2 * time.Second)\n}\nif err != nil { return err }","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Wrap the wait so a single failed poll doesn't kill a queued run.\nerr := c.WaitForTestRunReady(ctx, testRunID, poll)\nif err != nil && !strings.Contains(err.Error(), \"aborted before starting\") &&\n\t!strings.Contains(err.Error(), \"completed before\") && !errors.Is(err, context.Canceled) {\n\t// transient poll failure — bounded backoff retry\n\tfor i := 1; i <= 2 && err != nil; i++ {\n\t\ttime.Sleep(time.Duration(i*3) * time.Second)\n\t\terr = c.WaitForTestRunReady(ctx, testRunID, poll)\n\t}\n}","preventionTips":["Use a long-lived, permission-stable token when queue waits can be long.","Keep network connectivity stable during the wait phase; avoid killing Wi-Fi/VPN mid-poll.","Record the test run ID at start so a failed poll can be inspected in the UI afterwards."],"tags":["k6","cloud","provisioning","polling","v6-api","test-run"],"backgroundTag":"api-request-failed","analyzedSha":"01ffac6f245854c1b8adc6a69857c76a15f90022","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}