projectdiscovery/nuclei · error
grpc: dialers not initialized for executionId %q
Error message
grpc: dialers not initialized for executionId %q
What it means
An executionId was present but protocolstate.GetDialersWithId(executionID) returned nil or lacked Fastdialer: no per-execution dialer set is registered (never initialized, already torn down, or an id mismatch), so the gRPC connection cannot be routed through fastdialer and host policy.
Source
Thrown at pkg/js/libs/grpc/invoke.go:58
// (instead of gRPC's built in DNS resolver), keeping resolution and policy
// enforcement inside fastdialer.
func dialTarget(ctx context.Context, executionID, target string, cfg connConfig) (*grpc.ClientConn, error) {
host, _, err := net.SplitHostPort(target)
if err != nil {
return nil, fmt.Errorf("invalid grpc target %q (expected host:port): %w", target, err)
}
if host == "" {
return nil, fmt.Errorf("grpc target host cannot be empty")
}
if executionID == "" {
return nil, fmt.Errorf("grpc: refusing to dial without executionId")
}
if !protocolstate.IsHostAllowed(executionID, host) {
return nil, protocolstate.ErrHostDenied.Msgf(host)
}
dialers := protocolstate.GetDialersWithId(executionID)
if dialers == nil || dialers.Fastdialer == nil {
return nil, fmt.Errorf("grpc: dialers not initialized for executionId %q", executionID)
}
contextDialer := func(dialCtx context.Context, addr string) (net.Conn, error) {
return dialers.Fastdialer.Dial(dialCtx, "tcp", addr)
}
var creds credentials.TransportCredentials
if cfg.plaintext {
creds = insecure.NewCredentials()
} else {
serverName := cfg.serverName
if serverName == "" {
serverName = host
}
creds = credentials.NewTLS(&tls.Config{
InsecureSkipVerify: cfg.insecureSkipVerify,
ServerName: serverName,
MinVersion: tls.VersionTLS12,View on GitHub (pinned to 265b3a3dec)
Solutions
- Create the grpc.Client, invoke, and Close() within a single execution lifetime
- Ensure protocolstate dialers are initialized for the execution before the first Connect/Invoke
- Do not cache clients across scans; re-create them per execution
Example fix
// before: client cached across executions, dialers of the first execution long gone
let cached;
function probe(t) {
cached = cached || new grpc.Client(t);
return cached.Invoke('acme.v1.Svc/Get', '{}');
}
// after: construct, use, close inside one execution
function probe(t) {
const c = new grpc.Client(t);
try { return c.Invoke('acme.v1.Svc/Get', '{}'); }
finally { c.Close(); }
} Defensive patterns
Strategy: try-catch
Try / catch
try {
const c = new grpc.Client(target, opts);
try { return c.Invoke(method, msg); } finally { c.Close(); }
} catch (e) {
if (/dialers not initialized/.test(e.message || '')) {
// execution lifecycle bug: re-create the client inside a live execution; do not cache across scans
}
} Prevention
- Construct, use, and Close() the grpc.Client within a single execution
- Never cache clients in module-level variables across template runs
- Ensure protocolstate is initialized before the first Connect/Invoke
When it happens
Trigger: Client.Connect()/Invoke() racing with execution teardown; an SDK flow that sets an executionId but skips protocolstate initialization; a Client cached from a previous execution being reused after its dialers were released.
Common situations: Holding a grpc.Client in a module-level variable across template executions; embedded nuclei flows that bypass the standard engine initialization; cleanup goroutines closing dialers while reflection is still running.
Related errors
- grpc: refusing to dial without executionId
- dialers not initialized for %s
- goimpacket: no fastdialer registered for executionId %q
- invalid grpc target %q (expected host:port): %w
- grpc target host cannot be empty
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/aa43de3dc9521ce7.
Report an issue: GitHub.