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
- Move client.load(...) to the top level of the script, right after creating the client with new grpc.Client().
- Keep connect() in the VU context (or use setup for a shared connection) but always load protos at init.
- 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
- Remember the lifecycle split: load at init, connect/invoke at runtime.
- Keep one client per script, constructed and loaded at module scope.
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
- missing init environment
- canceled by client (k6)
- csv Parser constructor must be called in the init context
- encrypted pkcs8 formatted key is not supported
- failed to decode PEM key
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)