{"record":{"id":"a5afdb5d227bcd33","repo":"grafana/k6","slug":"no-grpc-connection-you-must-call-connect-first","errorCode":null,"errorMessage":"no gRPC connection, you must call connect first","messagePattern":"no gRPC connection, you must call connect first","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/grpc/client.go","lineNumber":356,"sourceCode":"\t}()\n\n\treturn promise, nil\n}\n\n// buildInvokeRequest creates a new InvokeRequest from the given method name, request object and parameters\nfunc (c *Client) buildInvokeRequest(\n\tmethod string,\n\treq sobek.Value,\n\tparams sobek.Value,\n) (grpcext.InvokeRequest, error) {\n\tgrpcReq := grpcext.InvokeRequest{}\n\n\tstate := c.vu.State()\n\tif state == nil {\n\t\treturn grpcReq, common.NewInitContextError(\"invoking RPC methods in the init context is not supported\")\n\t}\n\tif c.conn == nil {\n\t\treturn grpcReq, errors.New(\"no gRPC connection, you must call connect first\")\n\t}\n\tif method == \"\" {\n\t\treturn grpcReq, errors.New(\"method to invoke cannot be empty\")\n\t}\n\tif method[0] != '/' {\n\t\tmethod = \"/\" + method\n\t}\n\tmethodDesc := c.mds[method]\n\tif methodDesc == nil {\n\t\treturn grpcReq, fmt.Errorf(\"method %q not found in file descriptors\", method)\n\t}\n\n\tp, err := newCallParams(c.vu, params)\n\tif err != nil {\n\t\treturn grpcReq, fmt.Errorf(\"invalid GRPC's client.invoke() parameters: %w\", err)\n\t}\n\n\t// k6 GRPC Invoke's default timeout is 2 minutes","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/grpc/client.go#L338-L374","documentation":"Both client.invoke() and client.asyncInvoke() build the request via buildInvokeRequest, which requires an established connection (c.conn). c.conn is only set by client.connect() and cleared by client.close(), so invoking without a preceding successful connect throws this error.","triggerScenarios":"Calling client.invoke(...) before client.connect(...); calling invoke after client.close() (e.g. close moved into a shared helper or teardown ordering bug); connect() placed inside an if-branch or loop iteration that did not execute.","commonSituations":"Refactoring connect() into conditional initialization; closing the client in one scenario while another scenario or handleSummary-like code still invokes; copy-paste between scripts where connect was dropped.","solutions":["Call client.connect(addr) in the init context (or at the start of the default function) before any invoke","Ensure client.close() runs only after all invokes (end of default function or teardown)","Check that no early return, exception, or branch skips the connect call on any code path"],"exampleFix":"// before\nexport default function () {\n  client.invoke('/hello.HelloService/SayHello', {});\n}\n\n// after\nclient.connect('grpcbin.example.com:443');\nexport default function () {\n  client.invoke('/hello.HelloService/SayHello', {});\n}","handlingStrategy":"validation","validationCode":"const client = new grpc.Client();\nclient.connect(addr);\n// or, guarding a shared helper:\nfunction ensureConnected(client, addr) {\n  if (!client || !isConnected) { client.connect(addr); isConnected = true; }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Connect in the init context immediately after creating the client; close only in teardown or at the very end","Track connection state in one variable so reconnect logic cannot invoke first","Beware exceptions between connect and invoke -- a failed connect leaves conn nil"],"tags":["grpc","connection","ordering","invoke"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}