projectdiscovery/nuclei · critical

dialers not initialized for execution id: {ExecutionId}

Error message

dialers not initialized for execution id: {ExecutionId}

What it means

GetRawHTTP panicked because no dialers were registered for options.Options.ExecutionId. HTTP raw requests go through the per-execution Dialers struct; GetDialersWithId returning nil means protocolstate.Init(types.Options) was never called with that ExecutionId (or the entry was deleted). The panic also guards a LazyLock-protected RawHTTPClient cache further down, but firing here means initialization ordering is wrong.

Source

Thrown at pkg/protocols/http/httpclientpool/clientpool.go:257

			builder.WriteString(c.Connection.CustomMaxTimeout.String())
		}
	}
	builder.WriteString("r")
	builder.WriteString(strconv.FormatInt(int64(c.ResponseHeaderTimeout.Seconds()), 10))
	hash := builder.String()
	return hash
}

// HasStandardOptions checks whether the configuration requires custom settings
func (c *Configuration) HasStandardOptions() bool {
	return c.Threads == 0 && c.MaxRedirects == 0 && c.RedirectFlow == DontFollowRedirect && c.DisableCookie && c.Connection == nil && !c.NoTimeout && c.ResponseHeaderTimeout == 0
}

// GetRawHTTP returns the rawhttp request client
func GetRawHTTP(options *protocols.ExecutorOptions) *rawhttp.Client {
	dialers := protocolstate.GetDialersWithId(options.Options.ExecutionId)
	if dialers == nil {
		panic("dialers not initialized for execution id: " + options.Options.ExecutionId)
	}

	// Lock the dialers to avoid a race when setting RawHTTPClient
	dialers.Lock()
	defer dialers.Unlock()

	if dialers.RawHTTPClient != nil {
		return dialers.RawHTTPClient
	}

	rawHttpOptionsCopy := *rawhttp.DefaultOptions
	if options.Options.AliveHttpProxy != "" {
		rawHttpOptionsCopy.Proxy = options.Options.AliveHttpProxy
	} else if options.Options.AliveSocksProxy != "" {
		rawHttpOptionsCopy.Proxy = options.Options.AliveSocksProxy
	} else if dialers.Fastdialer != nil {
		rawHttpOptionsCopy.FastDialer = dialers.Fastdialer
	}

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Call protocolstate.Init(options) with the same Options whose ExecutionId is later found in ExecutorOptions.Options before issuing raw HTTP requests.
  2. Use the standard runner (internal/runner) which performs Init up front.
  3. Validate with protocolstate.ShouldInit(options.Options.ExecutionId) at the SDK boundary.
  4. Never build ExecutorOptions with an ad-hoc ExecutionId that was never registered.

Example fix

// before
client := httpclientpool.GetRawHTTP(exOpts) // panics

// after
if protocolstate.ShouldInit(exOpts.Options.ExecutionId) {
    if err := protocolstate.Init(exOpts.Options); err != nil { return err }
}
client := httpclientpool.GetRawHTTP(exOpts)
Defensive patterns

Strategy: validation

Validate before calling

if protocolstate.ShouldInit(exOpts.Options.ExecutionId) {
    if err := protocolstate.Init(exOpts.Options); err != nil { return err }
}
client := httpclientpool.GetRawHTTP(exOpts)

Try / catch

// panic by contract: validating dialer registration beforehand is the only correct guard.

Prevention

When it happens

Trigger: Calling a raw-HTTP request path (e.g. unsafe/raw requests in templates) as an SDK consumer before protocolstate.Init registered dialers for the active ExecutionId; mixing execution ids between options used at Init time and options passed to GetRawHTTP.

Common situations: Embedding nuclei via lib/ and constructing protocol executors manually; reusing Configuration/ExecutorOptions objects across runs with fresh execution ids; refactors that changed where Init happens relative to first raw request compilation.

Related errors


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