grafana/k6 · error
failed to parse retryable status codes: %w
Error message
failed to parse retryable status codes: %w
What it means
retryableStatusCodes (internal/cloudapi/insights/client.go:271-286) splits the comma-separated RetryableStatusCodes string and parses each entry with codes.Code.UnmarshalJSON. An empty string fails with 'no retryable status codes provided'; an unrecognized name fails with 'invalid status code X provided'. Entries must be exact gRPC code names like 'Unavailable', 'DeadlineExceeded', 'ResourceExhausted'.
Source
Thrown at internal/cloudapi/insights/client.go:253
if cfg.AuthConfig.Enabled {
opts = append(opts, grpc.WithPerRPCCredentials(newPerRPCCredentials(cfg.AuthConfig)))
}
rI, err := retryInterceptor(cfg.RetryConfig)
if err != nil {
return nil, fmt.Errorf("failed to create retry interceptors: %w", err)
}
opts = append(opts, grpc.WithChainUnaryInterceptor([]grpc.UnaryClientInterceptor{rI}...))
return opts, nil
}
func retryInterceptor(retryConfig ClientRetryConfig) (grpc.UnaryClientInterceptor, error) {
rSC, err := retryableStatusCodes(retryConfig.RetryableStatusCodes)
if err != nil {
return nil, fmt.Errorf("failed to parse retryable status codes: %w", err)
}
withCodes := grpcRetry.WithCodes(rSC...)
withMax := grpcRetry.WithMax(retryConfig.MaxAttempts)
withPerRetryTimeout := grpcRetry.WithPerRetryTimeout(retryConfig.PerRetryTimeout)
callOptions := []grpcRetry.CallOption{withCodes, withMax, withPerRetryTimeout}
backoffConfig := retryConfig.BackoffConfig
if backoffConfig.Enabled {
backoff := grpcRetry.WithBackoff(
grpcRetry.BackoffExponentialWithJitter(backoffConfig.WaitBetween, backoffConfig.JitterFraction))
callOptions = append(callOptions, backoff)
}
unaryInterceptor := grpcRetry.UnaryClientInterceptor(callOptions...)
return unaryInterceptor, nil
}
func retryableStatusCodes(retryableStatusCodes string) ([]codes.Code, error) {View on GitHub (pinned to 93accf6570)
Solutions
- Use exact CamelCase gRPC code names, comma-separated with no extra whitespace: 'Unavailable,Internal,ResourceExhausted'
- If you do not need custom retry codes, leave the field at its default rather than setting it to an empty string
- Echo the final value right before k6 starts to catch CI interpolation artifacts
Example fix
// before (Go embedder) cfg.RetryConfig.RetryableStatusCodes = "resource_exhausted" // snake_case: invalid // after cfg.RetryConfig.RetryableStatusCodes = "Unavailable,ResourceExhausted"
Defensive patterns
Strategy: validation
Validate before calling
// Go: validate the config string before constructing the insights client
var validCodes = map[string]bool{"OK": true, "Canceled": true, "Unknown": true, "InvalidArgument": true, "DeadlineExceeded": true, "NotFound": true, "AlreadyExists": true, "PermissionDenied": true, "ResourceExhausted": true, "FailedPrecondition": true, "Aborted": true, "OutOfRange": true, "Unimplemented": true, "Internal": true, "Unavailable": true, "DataLoss": true, "Unauthenticated": true}
func retryCodesValid(s string) error {
if strings.TrimSpace(s) == "" {
return fmt.Errorf("retryable status codes must not be empty")
}
for _, c := range strings.Split(s, ",") {
if !validCodes[strings.TrimSpace(c)] {
return fmt.Errorf("invalid gRPC code %q", c)
}
}
return nil
} Prevention
- Copy code names from the gRPC codes documentation verbatim - case is significant
- Add a unit test asserting your config template's retry codes parse
- Treat 'failed to parse retryable status codes' as config, never as network - do not blind-retry
When it happens
Trigger: Passing an empty retryable-codes value in the insights client config; a typo such as 'DeadLineExceeded', snake_case 'resource_exhausted', or stray whitespace after a comma; a list built by joining with the wrong separator.
Common situations: Hand-written env overrides in CI; configuration generated from YAML maps that mangle case; defaults overridden by an orchestration layer that assumes a different format.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to create retry interceptors: %w
- failed to create dial options: %w
- failed to load TLS credentials from file: %w
- 104
- method to invoke cannot be empty
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/41b8de12083701e5.
Report an issue: GitHub.