grafana/k6 · error

couldn't open protoset: %w

Error message

couldn't open protoset: %w

What it means

grpc.Client.load() (init context only) resolves the protoset path against the script's directory and opens it from the init filesystem (initEnv.FileSystems['file']); an Open failure is wrapped as 'couldn't open protoset' (internal/js/modules/k6/grpc/client.go:107). This is purely a file-location problem - distinct from read or unmarshal failures on the same call.

Source

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

	return c.convertToMethodInfo(fdset)
}

// LoadProtoset will parse the given protoset file (serialized FileDescriptorSet) and make the file
// descriptors available to request.
func (c *Client) LoadProtoset(protosetPath 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")
	}

	absFilePath := initEnv.GetAbsFilePath(protosetPath)
	fdsetFile, err := initEnv.FileSystems["file"].Open(absFilePath)
	if err != nil {
		return nil, fmt.Errorf("couldn't open protoset: %w", err)
	}

	defer func() { _ = fdsetFile.Close() }()
	fdsetBytes, err := io.ReadAll(fdsetFile)
	if err != nil {
		return nil, fmt.Errorf("couldn't read protoset: %w", err)
	}

	fdset := &descriptorpb.FileDescriptorSet{}
	if err = proto.Unmarshal(fdsetBytes, fdset); err != nil {
		return nil, fmt.Errorf("couldn't unmarshal protoset file %s: %w", protosetPath, err)
	}

	return c.convertToMethodInfo(fdset)
}

// Note: this function was lifted from `lib/options.go`
func decryptPrivateKey(key, password []byte) ([]byte, error) {

View on GitHub (pinned to 93accf6570)

Solutions

  1. Verify the path relative to the script file (not the CWD) and its exact case
  2. Ensure the protoset is deployed with the script (COPY in Docker, included when archiving)
  3. Drop the protoset and use server reflection instead: client.connect(addr, { reflect: true })

Example fix

// before
const methods = grpcClient.load('../pb/service.protoset'); // wrong directory

// after
const methods = grpcClient.load('./pb/service.protoset');
// or skip the file entirely:
// grpcClient.connect(addr, { reflect: true })
Defensive patterns

Strategy: validation

Validate before calling

// fail fast with a clearer error before load()
try {
  open('./pb/service.protoset'); // sync builtin open(); throws if the file is missing
} catch (e) {
  throw new Error(`protoset missing next to the script: ${e.message}`);
}
const methods = client.load('./pb/service.protoset');

Try / catch

try {
  client.load('./pb/service.protoset');
} catch (e) {
  if (/couldn't open protoset/.test(e.message)) { /* fix path or fall back to reflect: true */ }
  throw e;
}

Prevention

When it happens

Trigger: client.load('./service.protoset') where the file does not exist at the resolved path: typo, wrong case, file in another directory, or the protoset not shipped alongside the script (k6 archive, Docker image, CI checkout).

Common situations: Running k6 from a different working directory; CI jobs copying the script but not sibling files; Docker images missing the ADD/COPY for the protoset; case-sensitive filesystems exposing path-case mistakes.

Related errors


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