projectdiscovery/nuclei · error

could not create flow executor

Error message

could not create flow executor: %s

What it means

In pkg/tmplexec/exec.go (Execute path), nuclei builds a fresh FlowExecutor per target because the flow executor is tightly coupled with executor options and scan context. If flow.NewFlowExecutor returns an error, execution of this template/target stops with 'could not create flow executor: %s'. In practice the only error NewFlowExecutor returns is 'invalid request type %s' — a request whose Type() is not in the flow engine's supported switch.

Solutions

  1. Upgrade nuclei to the latest release so the flow engine's protocol switch matches current template capabilities
  2. Remove `flow:` from the template, or remove the request section whose protocol the flow engine cannot dispatch
  3. Run `nuclei -validate` and verify each protocol section in the template is one of: dns, file, http, offline-http, headless, tcp/network, ssl, websocket, whois, code, javascript
  4. If using the SDK with custom Request implementations, map them to a supported Type() or drop the flow path

Example fix

# before: flow + workflow-style or unsupported section
flow: |
  http() && ssl()
workflow:
  - template: other.yaml

# after: keep flow, move workflow logic into plain templates without flow
Defensive patterns

Strategy: try-catch

Validate before calling

// Before executing a flow template, verify every request type is dispatchable:
func flowDispatchable(reqs []protocols.Request) bool {
    for _, r := range reqs {
        switch r.Type() {
        case templateTypes.DNSProtocol, templateTypes.FileProtocol, templateTypes.HTTPProtocol,
            templateTypes.OfflineHTTPProtocol, templateTypes.HeadlessProtocol, templateTypes.NetworkProtocol,
            templateTypes.SSLProtocol, templateTypes.WebsocketProtocol, templateTypes.WHOISProtocol,
            templateTypes.CodeProtocol, templateTypes.JavascriptProtocol:
        default:
            return false
        }
    }
    return true
}

Try / catch

ok, err := executer.Execute(scanCtx)
if err != nil {
    if strings.Contains(err.Error(), "could not create flow executor") {
        // template/binary protocol-support mismatch: skip template, suggest upgrade
        log.Printf("skipping flow template %s (unsupported request type) — upgrade nuclei", tid)
        continue
    }
    return err
}

Prevention

When it happens

Trigger: A template with a `flow:` block that also contains a request section the flow engine's switch does not handle (WorkflowProtocol or InvalidProtocol types, or a protocol type added in a newer nuclei than the running binary supports). The compile-time check (error 461) only validates JS syntax, so this surfaces later at Execute time on the first target.

Common situations: Version skew: running a new community template with flow against an older nuclei binary whose flow switch lacked a protocol; SDK users assembling custom protocols.Request implementations and enabling options.Flow; editing a template to add an exotic protocol section while keeping `flow:`.

Related errors


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

Appendix: source

Thrown at pkg/tmplexec/exec.go:205

				matched.Store(true)
			} else {
				lastMatcherEvent = event
			}
		}
	}
	var errx error

	// Note: this is required for flow executor
	// flow executer is tightly coupled with lot of executor options
	// and map , wg and other types earlier we tried to use (compile once and run multiple times)
	// but it is causing lot of panic and nil pointer dereference issues
	// so in compile step earlier we compile it to validate javascript syntax and other things
	// and while executing we create new instance of flow executor everytime
	if e.options.Flow != "" {
		flowexec, err := flow.NewFlowExecutor(e.requests, ctx, e.options, executed, e.program)
		if err != nil {
			ctx.LogError(err)
			return false, fmt.Errorf("could not create flow executor: %s", err)
		}
		if err := flowexec.Compile(); err != nil {
			ctx.LogError(err)
			return false, err
		}
		errx = flowexec.ExecuteWithResults(ctx)
	} else {
		errx = e.engine.ExecuteWithResults(ctx)
	}
	ctx.LogError(errx)

	if lastMatcherEvent != nil {
		lastMatcherEvent.Lock()
		defer lastMatcherEvent.Unlock()

		lastMatcherEvent.InternalEvent["error"] = getErrorCause(ctx.GenerateErrorMessage())

		writeFailureCallback(lastMatcherEvent, e.options.Options.MatcherStatus)

View on GitHub (pinned to 265b3a3dec)