Skip to content

k6 gRPC Client Connection Error Fix

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about k6 grpc client connection error fix. We cover key concepts, practical examples, and best practices.

Your k6 gRPC client throws rpc error: code = Unavailable desc = connection closed — the gRPC server is unreachable, TLS is misconfigured, or the protobuf definition is not loaded correctly for the k6/net/grpc module.

The Problem

import grpc from 'k6/net/grpc';

const client = new grpc.Client();

export default function () {
  client.connect('localhost:50051');
  const response = client.invoke('users.UserService/GetUser', { id: 1 });
  console.log(response);
}
ERRO[0001] rpc error: code = Unavailable desc = connection closed
  at invoke (4)

The gRPC server is not running on localhost:50051, TLS is required but not configured, or the protobuf service definition is missing.

Step-by-Step Fix

1. Load protobuf definitions

import grpc from 'k6/net/grpc';
import { check } from 'k6';

const client = new grpc.Client();
client.load(['./protos'], 'user_service.proto');

export default function () {
  client.connect('localhost:50051', { plaintext: true });
  const response = client.invoke('users.UserService/GetUser', { id: 1 });

  check(response, {
    'status is OK': (r) => r.status === grpc.StatusOK,
  });

  console.log(`User: ${response.message.name}`);
  client.close();
}

2. Configure TLS

import grpc from 'k6/net/grpc';

const client = new grpc.Client();
client.load(['./protos'], 'user_service.proto');

export default function () {
  // Without TLS (development)
  client.connect('localhost:50051', { plaintext: true });

  // With TLS (production)
  client.connect('api.example.com:443', {
    plaintext: false,
    tls: {
      domains: ['api.example.com'],
    },
  });

  const response = client.invoke('users.UserService/GetUser', { id: 1 });
  client.close();
}

3. Handle gRPC errors

import grpc from 'k6/net/grpc';
import { check } from 'k6';

const client = new grpc.Client();
client.load(['./protos'], 'user_service.proto');

export default function () {
  client.connect('localhost:50051', { plaintext: true });

  try {
    const response = client.invoke('users.UserService/GetUser', { id: 1 }, {
      timeout: '5s',
    });

    check(response, {
      'status is OK': (r) => r.status === grpc.StatusOK,
    });

    if (response.status !== grpc.StatusOK) {
      console.log(`gRPC error: ${response.error}`);
    }
  } catch (e) {
    console.log(`Exception: ${e}`);
  } finally {
    client.close();
  }
}

4. Use gRPC streaming

import grpc from 'k6/net/grpc';
import { check } from 'k6';

const client = new grpc.Client();
client.load(['./protos'], 'user_service.proto');

export default function () {
  client.connect('localhost:50051', { plaintext: true });

  const stream = client.invoke('users.UserService/ListUsers', {
    page_size: 10,
  }, {
    timeout: '10s',
  });

  stream.on('data', function (data) {
    console.log(`User: ${data.name}`);
  });

  stream.on('end', function () {
    console.log('Stream ended');
  });

  client.close();
}

5. Set gRPC options and metadata

import grpc from 'k6/net/grpc';

const client = new grpc.Client();
client.load(['./protos'], 'user_service.proto');

export default function () {
  client.connect('localhost:50051', {
    plaintext: true,
    maxReceiveSize: 10 * 1024 * 1024,  // 10 MB
  });

  const response = client.invoke('users.UserService/GetUser', { id: 1 }, {
    metadata: {
      'authorization': `Bearer ${__ENV.TOKEN}`,
      'x-request-id': `test-${__VU}-${__ITER}`,
    },
tags: { endpoint: 'GetUser' },
  });

  client.close();
}

Expected output:

     ✓ status is OK

     gRPC response: {"id":1,"name":"Alice","email":"alice@example.com"}

Prevention Tips

  • Load protobuf files with client.load() before making any RPC calls
  • Use { plaintext: true } for local development, TLS for production
  • Handle gRPC errors with try/catch and status code checks
  • Close the client connection after each VU iteration
  • Use tags for per-endpoint gRPC metrics

Common Mistakes with grpc

  1. Non-exhaustive pattern matches that compile with warnings then crash at runtime
  2. Misunderstanding that String is [Char] with poor performance for large text operations
  3. Using foldl instead of foldl' causing stack overflow on large lists

These mistakes appear frequently in real-world K6 code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### Can k6 load protobuf definitions from a remote registry?

No, k6 loads .proto files from the local filesystem only. Use client.load(['dir'], 'file.proto') or client.load(null, 'content') with the protobuf content as a string. For remote registries, download the .proto files before the test.

Does k6 support gRPC reflection?

Yes, if the server supports gRPC reflection (via reflectionv1alpha), you can omit client.load() and use reflection to discover services. Enable reflection on the server and call client.connectWithReflection() instead.

What performance considerations apply to gRPC tests?

gRPC uses HTTP/2 multiplexing — one connection can handle many concurrent streams. However, each client.connect() creates a new HTTP/2 connection. For high-VU tests, consider connection pooling or reusing connections across iterations.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro