grafana/k6 · error

load must be called in the init context

Error message

load must be called in the init context

What it means

grpc.Client.Load parses .proto files and builds method descriptors, which is intentionally restricted to the init context: parsing protos per-iteration would be wasteful and the descriptors are shared VU-wide. The guard rejects the call whenever vu.State() is non-nil, i.e. whenever it runs inside a VU iteration.

Source

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

	"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 93accf6570)

Solutions

  1. Move client.load() to the top level of the script, right after creating the client
  2. Keep proto filenames static or derive them from exported options (available in init) rather than runtime state
  3. If descriptors must vary, load all variants in init and select per iteration

Example fix

// before
export default function () {
  client.load([], 'protos/hello.proto');
  client.invoke('/hello.HelloService/SayHello', {});
}

// after
client.load([], 'protos/hello.proto');
export default function () {
  client.invoke('/hello.HelloService/SayHello', {});
}
Defensive patterns

Strategy: validation

Validate before calling

// Static convention: client creation + load() live at module top level.
// If filenames come from config, resolve them in init:
import { options } from './config.js'; // plain module data, readable in init
client.load(options.protoImportPaths, ...options.protoFiles);

Prevention

When it happens

Trigger: Calling client.load(importPaths, ...protoFiles) inside export default function(); calling it from setup()/teardown() or any helper invoked during iterations.

Common situations: Refactoring scripts so proto loading moves into a function called from the default function; attempting to load protos conditionally based on runtime state; code shared between init and VU phases.

Related errors


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