projectdiscovery/nuclei · error

goimpacket: no fastdialer registered for executionId %q

Error message

goimpacket: no fastdialer registered for executionId %q

What it means

The context carried an executionId, but protocolstate.GetDialersWithId(execID) returned nil (or a record without Fastdialer), meaning no dialer set was registered for that execution. The exec-bound dial path refuses to fall back to a default dialer, so it fails closed instead of bypassing fastdialer and host policy.

Source

Thrown at pkg/js/libs/gptransport/dialer.go:55

	return &gptr.Dialer{
		DialFn: func(ctx context.Context, network, address string) (net.Conn, error) {
			return DialWithExec(ctx, execID, network, address)
		},
	}
}

// DialWithExec performs the fastdialer dial after enforcing host policy.
func DialWithExec(ctx context.Context, execID, network, address string) (net.Conn, error) {
	host, _, err := net.SplitHostPort(address)
	if err != nil {
		return nil, fmt.Errorf("invalid address %q: %w", address, err)
	}
	if !protocolstate.IsHostAllowed(execID, host) {
		return nil, protocolstate.ErrHostDenied.Msgf(host)
	}
	dialer := protocolstate.GetDialersWithId(execID)
	if dialer == nil || dialer.Fastdialer == nil {
		return nil, fmt.Errorf("goimpacket: no fastdialer registered for executionId %q", execID)
	}
	return dialer.Fastdialer.Dial(ctx, network, address)
}

// ExecutionIDFromCtx pulls the executionId set by nuclei on its goja runtime
// or scan context. Returns "" when the context carries no id.
func ExecutionIDFromCtx(ctx context.Context) string {
	if ctx == nil {
		return ""
	}
	if v := ctx.Value("executionId"); v != nil {
		if id, ok := v.(string); ok {
			return id
		}
	}
	return ""
}

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Initialize/verify protocolstate dialers for the executionId before starting any work that dials
  2. Cancel or complete in-flight goimpacket work before the execution's dialers are released
  3. Log the execID at both registration and dial time and confirm they match exactly

Example fix

// before: dials before dialers exist (or after they were released)
conn, err := gptransport.DialWithExec(ctx, execID, "tcp", addr)

// after: fail fast with a lifecycle hint instead of an opaque dial error
if d := protocolstate.GetDialersWithId(execID); d == nil || d.Fastdialer == nil {
	return fmt.Errorf("dialers not registered for execution %s; initialize protocolstate first", execID)
}
conn, err := gptransport.DialWithExec(ctx, execID, "tcp", addr)
Defensive patterns

Strategy: validation

Validate before calling

if d := protocolstate.GetDialersWithId(execID); d == nil || d.Fastdialer == nil {
    return fmt.Errorf("no dialers registered for execution %s; initialize protocolstate first", execID)
}
conn, err := gptransport.DialWithExec(ctx, execID, "tcp", addr)

Type guard

func hasRegisteredDialers(execID string) bool {
    d := protocolstate.GetDialersWithId(execID)
    return d != nil && d.Fastdialer != nil
}

Try / catch

if err != nil && strings.Contains(err.Error(), "no fastdialer registered") {
    // lifecycle bug: dialers uninitialized or released; re-init or abort, do not silently retry

Prevention

When it happens

Trigger: protocolstate was never initialized for the execution; the execution already finished and its dialers were released while a goroutine still dials; the executionId in the context does not match the id used at registration time.

Common situations: Long-lived background goroutines surviving past scan teardown; SDK users inventing their own execution ids without registering dialers; races between execution cleanup and in-flight SMB/DCOM operations.

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/2abd7ad55a8a671e. Report an issue: GitHub.