projectdiscovery/nuclei · error
panic occurred while executing flow: %v
Error message
panic occurred while executing flow: %v
What it means
A deferred recover inside FlowExecutor.Execute (pkg/tmplexec/flow/flow_executor.go:254, marked TODO for RCA) catches panics raised while the goja runtime executes the flow, logs them via ctx.LogError, and lets the scan continue. Notably the recover is skipped when running under CI (ci.IsCI()), so the same panic crashes the binary in CI — by design, to surface bugs there.
Source
Thrown at pkg/tmplexec/flow/flow_executor.go:254
}()
defer func() {
// remove set builtin
_ = runtime.GlobalObject().Delete("set")
_ = runtime.GlobalObject().Delete("template")
for proto := range f.protoFunctions {
_ = runtime.GlobalObject().Delete(proto)
}
runtime.RemoveContextValue("executionId")
}()
// TODO(dwisiswant0): remove this once we get the RCA.
defer func() {
if ci.IsCI() {
return
}
if r := recover(); r != nil {
f.ctx.LogError(fmt.Errorf("panic occurred while executing flow: %v", r))
}
}()
if ctx.OnResult == nil {
return fmt.Errorf("output callback cannot be nil")
}
// before running register set of builtins
if err := runtime.Set("set", func(call goja.FunctionCall) goja.Value {
varName := call.Argument(0).Export()
varValue := call.Argument(1).Export()
f.options.GetTemplateCtx(f.ctx.Input.MetaInput).Set(types.ToString(varName), varValue)
return goja.Null()
}); err != nil {
return err
}
// also register functions that allow executing protocols from js
for proto, fn := range f.protoFunctions {
if err := runtime.Set(proto, fn); err != nil {View on GitHub (pinned to 265b3a3dec)
Solutions
- Upgrade nuclei to the latest patch release — flow panic fixes land frequently
- Reduce the template to the smallest flow that still panics (drop protocol sections one by one) and re-run to isolate the trigger
- Run the repro in CI or with CI=true env to get a full goroutine dump instead of the recovered log line
- Report the issue to projectdiscovery/nuclei with the template YAML, nuclei -version, and the panic value from the log
Defensive patterns
Strategy: try-catch
Try / catch
// The engine already recovers outside CI; at the call site keep per-template isolation:
func runTemplateSafe(e protocols.Executer, ctx *scan.ScanContext) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered flow panic: %v", r)
}
}()
return e.Execute(ctx)
} Prevention
- Run the newest nuclei patch release; flow panics get fixed quickly
- Reproduce panics with CI=true to get a full stack instead of the one-line recovered log
- Isolate flow templates in batch runners so one panic cannot stall the whole scan
- Keep a known-good version pinned for production, test template updates in staging
When it happens
Trigger: Any runtime panic inside flow execution: nil map or nil pointer dereference in a protocol callback, an unexpected type crossing the JS/Go boundary in the flow runtime, or a bug in the flow engine itself. It fires only on the non-CLI-interactive path where a template with `flow:` executes against a target and something panics mid-program.
Common situations: A flow template exercising an engine edge case (nil template ctx value, exotic extractor output fed back into flow); upgrading templates without upgrading nuclei; SDK reuse of a template executer across goroutines it wasn't built for.
Related errors
- panic: %s
- nuclei js runtime: program failed to terminate after interru
- krbroast: no executionId on goja runtime
- ipv4 and/or ipv6 must be selected
- encountered errors while performing template validation
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/36f523ddf2aa8abe.
Report an issue: GitHub.