projectdiscovery/nuclei · critical

dialers with executionId {executionId} not found

Error message

dialers with executionId {executionId} not found

What it means

The built-in isPortOpen TCP helper panicked because the execution context's dialers were missing. scripts.go fetches executionId from the JS context, looks up protocolstate.GetDialersWithId(executionId), and panics when nil — meaning protocolstate.Init never registered dialers for that execution id before the JS template ran.

Source

Thrown at pkg/js/global/scripts.go:133

		Name: "isPortOpen",
		Signatures: []string{
			"isPortOpen(host string, port string, [timeout int]) bool",
		},
		Description: "isPortOpen checks if given TCP port is open on host. timeout is optional and defaults to 5 seconds",
		FuncDecl: func(ctx context.Context, host string, port string, timeout ...int) (bool, error) {
			if len(timeout) > 0 {
				var cancel context.CancelFunc
				ctx, cancel = context.WithTimeout(ctx, time.Duration(timeout[0])*time.Second)
				defer cancel()
			}
			if host == "" || port == "" {
				return false, errkit.New("isPortOpen: host or port is empty")
			}

			executionId := ctx.Value("executionId").(string)
			dialer := protocolstate.GetDialersWithId(executionId)
			if dialer == nil {
				panic("dialers with executionId " + executionId + " not found")
			}

			conn, err := dialer.Fastdialer.Dial(ctx, "tcp", net.JoinHostPort(host, port))
			if err != nil {
				return false, err
			}
			_ = conn.Close()
			return true, nil
		},
	})

	_ = gojs.RegisterFuncWithSignature(runtime, gojs.FuncOpts{
		Name: "isUDPPortOpen",
		Signatures: []string{
			"isUDPPortOpen(host string, port string, [timeout int]) bool",
		},
		Description: "isUDPPortOpen checks if the given UDP port is open on the host. Timeout is optional and defaults to 5 seconds.",
		FuncDecl: func(ctx context.Context, host string, port string, timeout ...int) (bool, error) {

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Initialize dialers for the execution id before executing code-protocol templates: protocolstate.Init(opts) with opts.ExecutionId matching the one bound into the JS execution context.
  2. Prefer the standard runner path, which performs Init before any engine work.
  3. Guard with protocolstate.ShouldInit(executionId) at run start in SDK code paths.
  4. Ensure the ctx passed to the JS execution actually carries the same executionId value used at Init.

Example fix

// before
results, _ := engine.ExecuteWithResults(...) // JS calls IsPortOpen -> panic

// after
if protocolstate.ShouldInit(opts.ExecutionId) {
    if err := protocolstate.Init(opts); err != nil { return err }
}
results, _ := engine.ExecuteWithResults(ctxWithExecID, ...)
Defensive patterns

Strategy: validation

Validate before calling

if protocolstate.ShouldInit(execID) {
    if err := protocolstate.Init(opts); err != nil { return err }
}
// then execute the code template with ctx carrying execID

Try / catch

// panic is a programming-error signal; fix init order rather than recovering.
// In tests: require.NoError around engine setup so the panic surfaces at the guilty step.

Prevention

When it happens

Trigger: Running a JS template that calls IsPortOpen(host, port[, timeout]) in an embedded/SDK context where dialers were not initialized for the current ExecutionId; a race where the JS engine pool executes a request before Init completed or after dialers were torn down; executionId absent from ctx so the lookup key is wrong.

Common situations: lib/nuclei integrations driving the code protocol manually; custom runners that create the JS runtime but skip the dialer lifecycle; nuclei upgrades that moved from global dialers to per-execution-id dialers exposing a missing init step in third-party runners.

Related errors


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