grafana/k6 · error

load must be called in the init context

Error message

load must be called in the init context

What it means

The gRPC Client.load(importPaths, ...filenames) parses .proto files into method descriptors so client.invoke() can marshal requests. Proto loading is expensive and the resulting descriptors must be identical for every VU, so load() only works during the init context: it refuses to run when c.vu.State() is non-nil, i.e. during setup/teardown/VU code.

Solutions

  1. Move client.load(...) to the top level of the script, right after creating the client with new grpc.Client().
  2. Keep connect() in the VU context (or use setup for a shared connection) but always load protos at init.
  3. If protos are large, load them once at init and pass the client via the module scope - never re-load per iteration.

Example fix

// before
export default function () {
  client.load('./proto/', 'hello.proto'); // throws
}

// after
const client = new grpc.Client();
client.load('./proto/', 'hello.proto');
export default function () { client.connect(...); client.invoke(...); }
Defensive patterns

Strategy: validation

Validate before calling

// proto loading is init-only: keep it next to the client construction
const client = new grpc.Client();
client.load(importPaths, ...protos); // top level only
export default function () { /* connect + invoke only */ }

Prevention

When it happens

Trigger: client.load(...) called inside export default function(), setup(), or any function invoked at runtime. Note connect/invoke are the opposite: they require a VU state and fail in init - load and invoke bracket the test lifecycle.

Common situations: Moving proto loading inside setup() to 'initialize once'; wrapping load in a helper that the default function calls; scripts converted from examples where load appears at the bottom of the file after exports (hoisting confusion in reorganized code).

Related errors


AI-assisted analysis of grafana/k6@8d06114777 (2026-08-18). Data as JSON: /api/errors/0f498bdfd4a46d42. Report an issue: GitHub.

Appendix: source

Thrown at internal/js/modules/k6/grpc/client.go:52

	"google.golang.org/protobuf/types/descriptorpb"
	"google.golang.org/protobuf/types/dynamicpb"
)

// Client represents a gRPC client that can be used to make RPC requests
type Client struct {
	mds  map[string]protoreflect.MethodDescriptor
	conn *grpcext.Conn
	vu   modules.VU
	addr string

	types    *protoregistry.Types
	typesMtx sync.Mutex
}

// Load will parse the given proto files and make the file descriptors available to request.
func (c *Client) Load(importPaths []string, filenames ...string) ([]MethodInfo, error) {
	if c.vu.State() != nil {
		return nil, errors.New("load must be called in the init context")
	}

	initEnv := c.vu.InitEnv()
	if initEnv == nil {
		return nil, errors.New("missing init environment")
	}

	// If no import paths are specified, use the current working directory
	if len(importPaths) == 0 {
		importPaths = append(importPaths, initEnv.CWD.Path)
	}

	for i, s := range importPaths {
		// Clean file scheme as it is the only supported scheme and the following APIs do not support them
		importPaths[i] = strings.TrimPrefix(s, "file://")
	}

	resolver := protocompile.WithStandardImports(&protocompile.SourceResolver{

View on GitHub (pinned to 8d06114777)