grafana/k6 · error

failed to create retry interceptors: %w

Error message

failed to create retry interceptors: %w

What it means

Dial option construction ends by building a retry interceptor from RetryConfig (internal/cloudapi/insights/client.go:240-243); any failure there is wrapped as 'failed to create retry interceptors'. The current only failure source is parsing RetryableStatusCodes (see the sibling 'failed to parse retryable status codes' error), so this message is effectively an outer wrapper telling you the insights retry configuration is invalid.

Source

Thrown at internal/cloudapi/insights/client.go:242

	} else {
		if cfg.TLSConfig.CertFile != "" {
			creds, err := credentials.NewClientTLSFromFile(cfg.TLSConfig.CertFile, "")
			if err != nil {
				return nil, fmt.Errorf("failed to load TLS credentials from file: %w", err)
			}
			opts = append(opts, grpc.WithTransportCredentials(creds))
		} else {
			opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{MinVersion: tls.VersionTLS13})))
		}
	}

	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

View on GitHub (pinned to 93accf6570)

Solutions

  1. Read the wrapped message - it states whether the list is empty or which entry is invalid
  2. Set a valid comma-separated list of gRPC code names, e.g. 'Unavailable,ResourceExhausted' (case matters)
  3. Remove the override entirely so the client uses its defaults
  4. Update k6 / the orchestrator so the emitted config matches the current schema

Example fix

# before
export K6_CLOUD_API_RETRY_RETRYABLE_STATUS_CODES='unavailable; resource_exhausted'

# after
export K6_CLOUD_API_RETRY_RETRYABLE_STATUS_CODES='Unavailable,ResourceExhausted'
Defensive patterns

Strategy: validation

Validate before calling

# reject invalid retry-code lists before launch
IFS=',' read -ra CODES <<< "$K6_CLOUD_API_RETRY_RETRYABLE_STATUS_CODES"
VALID='OK Canceled Unknown InvalidArgument DeadlineExceeded NotFound AlreadyExists PermissionDenied ResourceExhausted FailedPrecondition Aborted OutOfRange Unimplemented Internal Unavailable DataLoss Unauthenticated'
for c in "${CODES[@]}"; do
  [[ " $VALID " == *" $c "* ]] || { echo "invalid gRPC code: $c"; exit 1; }
done

Prevention

When it happens

Trigger: RetryConfig.RetryableStatusCodes empty (produces 'no retryable status codes provided') or containing a name that is not a valid gRPC code (produces 'invalid status code X provided'); config injected by an orchestrator or env override with a malformed list.

Common situations: Orchestrator-managed runs exporting a retry-codes env var with lowercase names, trailing spaces, or semicolons instead of commas; an override written for an older config schema.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/c9a7ad8e62e698fd. Report an issue: GitHub.