grafana/k6 · error
notify test run completed
Error message
notify test run completed: %w
What it means
Raised by provisioning.Client.NotifyTestRunCompleted after POST /provisioning/v1/test_runs/{id}/notify returned a non-2xx HTTP status. The provisioning package's CheckResponse(hr) inspects the response, so this error means the cloud API received the request but rejected it — the wrapped value is usually a ResponseError carrying status code and body. The notify call uses the scoped test-run token (injected via context), not the long-lived API token, so auth failures here implicate that scoped token, not your login token.
Solutions
- Verify testRunID and testRunToken come from the same StartLocalExecution response and were not reused from an earlier run
- Inspect the wrapped ResponseError body for the exact API reason (status code + message) before changing anything
- If 401/403, re-provision to obtain a fresh scoped token; do not substitute the global K6_CLOUD_TOKEN
- If 5xx persisted through SDK retries, retry NotifyTestRunCompleted after a short delay once the service recovers
Example fix
// before
_ = client.NotifyTestRunCompleted(ctx, staleRunID, staleToken, testErr)
// after
if err := client.NotifyTestRunCompleted(ctx, result.TestRunID, result.RuntimeConfig.Token, testErr); err != nil {
var respErr *cloudapi.ResponseError
if errors.As(err, &respErr) {
logger.Errorf("notify rejected: status=%d body=%s", respErr.Response.StatusCode, respErr.Error())
}
return err
} Defensive patterns
Strategy: retry
Validate before calling
// ensure the run + token pair is fresh before notifying
if result == nil || result.TestRunID == 0 {
return fmt.Errorf("cannot notify: no provisioned test run")
} Type guard
func isHTTPResponseError(err error) bool {
var re *cloudapiv6.ResponseError
return errors.As(err, &re)
} Try / catch
if err := client.NotifyTestRunCompleted(ctx, runID, token, testErr); err != nil {
var respErr *cloudapiv6.ResponseError
if errors.As(err, &respErr) && respErr.Response != nil && respErr.Response.StatusCode >= 500 {
time.Sleep(5 * time.Second) // SDK already retried 5xx; back off further
err = client.NotifyTestRunCompleted(ctx, runID, token, testErr)
}
if err != nil { logger.WithError(err).Warn("cloud notify failed; run may show as unfinished") }
} Prevention
- Propagate the scoped test-run token from ProvisionResult.RuntimeConfig instead of re-using the global API token
- Notify promptly after the run finishes so the scoped token and run window are still valid
- Log the response body on failure — it contains the cloud-side reason
When it happens
Trigger: Calling NotifyTestRunCompleted with an expired or wrong testRunToken (401/403), a testRunID that was never provisioned or already closed (404), a malformed notification model (400), or a 4xx from the provisioning API. 5xx is retried by the SDK, so surfacing here means retries were exhausted or the status was 4xx.
Common situations: Local-execution flow where the run took longer than the token/URL validity, re-using a testRunID from a previous run after the cloud run was deleted, clock skew, or a proxy stripping the Authorization header.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
AI-assisted analysis of grafana/k6@8d06114777 (2026-08-18).
Data as JSON: /api/errors/036b52e513d0dbb7.
Report an issue: GitHub.
Appendix: source
Thrown at internal/cloudapi/provisioning/notify.go:77
// Authorization: Bearer <token>.
scopedCtx := context.WithValue(ctx, k6cloud.ContextAccessToken, testRunToken)
model := k6cloud.NewScriptExecutionCompletedNotificationApiModel("script_execution_completed")
if ne := mapTestErrorToNotifyCode(testErr); ne != nil {
model.SetError(*k6cloud.NewScriptExecutionError(ne.Code, ne.Reason))
} else {
model.SetErrorNil()
}
hr, err := c.apiClient.ProvisioningAPI.
TestRunsNotify(scopedCtx, testRunID).
ScriptExecutionCompletedNotificationApiModel(model).
Execute()
defer closeResponse(hr, &err)
if hr != nil {
if respErr := CheckResponse(hr); respErr != nil {
return fmt.Errorf("notify test run completed: %w", respErr)
}
}
if err != nil {
return fmt.Errorf("notify test run completed: %w", err)
}
return nil
}
View on GitHub (pinned to 8d06114777)