{"record":{"id":"6de3b3b23cdd236b","repo":"projectdiscovery/nuclei","slug":"grpc-refusing-to-dial-without-executionid","errorCode":null,"errorMessage":"grpc: refusing to dial without executionId","messagePattern":"grpc: refusing to dial without executionId","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/js/libs/grpc/invoke.go","lineNumber":51,"sourceCode":"}\n\n// dialTarget builds a *grpc.ClientConn whose every connection is routed through\n// nuclei's network policy. The host is validated up front and the actual dial\n// is delegated to the execution's fastdialer via a custom context dialer, so\n// IP/host denylists and RestrictLocalNetworkAccess are always enforced. The\n// passthrough scheme guarantees the target is handed verbatim to our dialer\n// (instead of gRPC's built in DNS resolver), keeping resolution and policy\n// enforcement inside fastdialer.\nfunc dialTarget(ctx context.Context, executionID, target string, cfg connConfig) (*grpc.ClientConn, error) {\n\thost, _, err := net.SplitHostPort(target)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid grpc target %q (expected host:port): %w\", target, err)\n\t}\n\tif host == \"\" {\n\t\treturn nil, fmt.Errorf(\"grpc target host cannot be empty\")\n\t}\n\tif executionID == \"\" {\n\t\treturn nil, fmt.Errorf(\"grpc: refusing to dial without executionId\")\n\t}\n\tif !protocolstate.IsHostAllowed(executionID, host) {\n\t\treturn nil, protocolstate.ErrHostDenied.Msgf(host)\n\t}\n\tdialers := protocolstate.GetDialersWithId(executionID)\n\tif dialers == nil || dialers.Fastdialer == nil {\n\t\treturn nil, fmt.Errorf(\"grpc: dialers not initialized for executionId %q\", executionID)\n\t}\n\n\tcontextDialer := func(dialCtx context.Context, addr string) (net.Conn, error) {\n\t\treturn dialers.Fastdialer.Dial(dialCtx, \"tcp\", addr)\n\t}\n\n\tvar creds credentials.TransportCredentials\n\tif cfg.plaintext {\n\t\tcreds = insecure.NewCredentials()\n\t} else {\n\t\tserverName := cfg.serverName","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/projectdiscovery/nuclei/blob/265b3a3dec374741614e342f813c10f8b38d2bb7/pkg/js/libs/grpc/invoke.go#L33-L69","documentation":"dialTarget refuses to connect when the executionId is empty. The grpc.Client captures executionID from the goja runtime at construction (nj.ExecutionId()); if nuclei never seeded the runtime or context with an execution id — i.e. the library is used outside a scan execution — every dial fails closed rather than bypassing network policy.","triggerScenarios":"Driving pkg/js/libs/grpc from standalone Go code or unit tests where the goja runtime/context carries no execution id; constructing a Client before the execution context was attached to the runtime; reusing a Client object across runtimes.","commonSituations":"SDK/embedded usage (lib/) that instantiates the JS libraries outside a real scan; developers testing Client.Invoke directly without nuclei's runtime initialization.","solutions":["Run the gRPC code inside a nuclei code-protocol template so the engine attaches the executionId","From Go/tests, seed the runtime/context with the execution id the same way nuclei does before constructing the Client","Register protocolstate dialers for that same executionId, otherwise the next failure is 'dialers not initialized'"],"exampleFix":"// before: standalone test runtime, no execution bound to it\nconst client = new grpc.Client('grpc.acme.com:443');\nclient.Invoke('acme.v1.Svc/Get', '{}'); // -> refusing to dial without executionId\n\n// after: same code runs as a nuclei code template (code protocol),\n// which seeds the executionId on the goja runtime automatically\n// template: code:\n//   const grpc = require('nuclei/grpc');\n//   const c = new grpc.Client('grpc.acme.com:443');\n//   c.Invoke('acme.v1.Svc/Get', '{}');","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let client;\ntry {\n  client = new grpc.Client(target, opts);\n  client.Connect();\n} catch (e) {\n  if (/without executionId/.test(e.message || '')) {\n    // running outside a nuclei execution: move this code into a code-protocol template\n  }\n}","preventionTips":["Run gRPC code only inside nuclei code templates, where the engine seeds the executionId","From SDK/tests, replicate nuclei' runtime setup (execution id + protocolstate) before constructing clients","Do not instantiate the JS grpc library in fresh goja runtimes without the scan scaffolding"],"tags":["grpc","javascript","lifecycle","go"],"backgroundTag":null,"analyzedSha":"265b3a3dec374741614e342f813c10f8b38d2bb7","analyzedAt":"2026-08-15T20:05:51.855Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}