projectdiscovery/nuclei · error
grpc target host cannot be empty
Error message
grpc target host cannot be empty
What it means
net.SplitHostPort succeeded but the host portion is empty: the target starts with ':' (e.g. ':443' or ':50051'). There is no hostname to enforce the network policy (denylists, RestrictLocalNetworkAccess) against, so dialTarget refuses to continue.
Source
Thrown at pkg/js/libs/grpc/invoke.go:48
insecureSkipVerify bool
serverName string
maxRecvMsgSize int
}
// dialTarget builds a *grpc.ClientConn whose every connection is routed through
// nuclei's network policy. The host is validated up front and the actual dial
// is delegated to the execution's fastdialer via a custom context dialer, so
// IP/host denylists and RestrictLocalNetworkAccess are always enforced. The
// passthrough scheme guarantees the target is handed verbatim to our dialer
// (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 {View on GitHub (pinned to 265b3a3dec)
Solutions
- Supply a concrete host/IP: new grpc.Client('grpc.acme.com:443')
- Guard template flow: only construct the Client when the extracted host is non-empty
- Validate host and port separately in config before joining them
Example fix
// before
const client = new grpc.Client(`:${port}`);
// after: skip when the extractor found no host
if (!host) { return; }
const client = new grpc.Client(`${host}:${port}`); Defensive patterns
Strategy: validation
Validate before calling
const hostOf = (t) => /^\[.*\]/.test(t) ? t.slice(1, t.indexOf(']')) : String(t || '').split(':')[0];
if (!hostOf(target)) {
// extracted host empty: skip or fail the input, do not construct the client
} Type guard
const hasGrpcHost = (t) => !!t && !t.trim().startsWith(':') && /^[[\w.-]/.test(t.trim()); Try / catch
try { const c = new grpc.Client(target); }
catch (e) { if (/host cannot be empty/.test(e.message || '')) { /* fix the empty host variable */ } } Prevention
- Validate host and port as separate non-empty values in config before joining
- Skip the gRPC probe when the extractor produced no host
- Beware YAML defaults that leave host as an empty string
When it happens
Trigger: new grpc.Client(':443'); building the target by concatenation when the host variable is empty: `${host}:443` with host=''; whitespace-only host; ports-only config strings.
Common situations: Template extractors producing an empty host (match group missing); environment config with the host key unset but the port set; YAML anchors defaulting host to ''.
Related errors
- failed to build request parser: %w
- invalid goexec method arguments: %w
- invalid grpc target %q (expected host:port): %w
- grpc: refusing to dial without executionId
- grpc: dialers not initialized for executionId %q
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/a6aa9d80a572ef2f.
Report an issue: GitHub.