projectdiscovery/nuclei · warning
invalid workflow with no templates or tags
Error message
invalid workflow with no templates or tags
What it means
parseWorkflow (pkg/templates/workflows.go:29) requires every node of a `workflows:` definition to carry either a `template:` path or a non-empty `tags:` list; with both empty the node cannot resolve to any template and the error is returned. Subtemplates are parsed recursively by the same function, so nested entries must satisfy the same rule. Callers only log a warning ('Could not parse workflow ...') and skip the file/entry, so the scan proceeds without those templates rather than aborting.
Source
Thrown at pkg/templates/workflows.go:29
"github.com/projectdiscovery/nuclei/v3/pkg/workflows"
)
// compileWorkflow compiles the workflow for execution
func compileWorkflow(path string, preprocessor Preprocessor, options *protocols.ExecutorOptions, workflow *workflows.Workflow, loader model.WorkflowLoader) {
for _, workflow := range workflow.Workflows {
if err := parseWorkflow(preprocessor, workflow, options, loader); err != nil {
gologger.Warning().Msgf("Could not parse workflow %s: %v\n", path, err)
continue
}
}
}
// parseWorkflow parses and compiles all templates in a workflow recursively
func parseWorkflow(preprocessor Preprocessor, workflow *workflows.WorkflowTemplate, options *protocols.ExecutorOptions, loader model.WorkflowLoader) error {
shouldNotValidate := false
if workflow.Template == "" && workflow.Tags.IsEmpty() {
return errors.New("invalid workflow with no templates or tags")
}
if len(workflow.Subtemplates) > 0 || len(workflow.Matchers) > 0 {
shouldNotValidate = true
}
if err := parseWorkflowTemplate(workflow, preprocessor, options, loader, shouldNotValidate); err != nil {
return err
}
for _, subtemplates := range workflow.Subtemplates {
if err := parseWorkflow(preprocessor, subtemplates, options, loader); err != nil {
gologger.Warning().Msgf("Could not parse workflow: %v\n", err)
continue
}
}
for _, matcher := range workflow.Matchers {
if len(matcher.Name.ToSlice()) > 0 {
if err := matcher.Compile(); err != nil {
return errors.Wrap(err, "could not compile workflow matcher")
}View on GitHub (pinned to 265b3a3dec)
Solutions
- Add either `template: path/to/template.yaml` or `tags: ['some','tags']` to the offending entry
- Fix indentation so every subtemplate sits under a parent that itself has template or tags
- Run `nuclei -validate` against the workflow to pinpoint the file and entry
- Confirm tags is a real YAML list, not an empty scalar
Example fix
# before
workflows:
- subtemplates:
- tags: rce
# after
workflows:
- template: cves/2021/CVE-2021-xxx.yaml
subtemplates:
- tags: rce Defensive patterns
Strategy: validation
Validate before calling
// before running a workflow, assert every node resolves
func workflowNodesValid(wfs []workflows.WorkflowTemplate) error {
for _, w := range wfs {
if w.Template == "" && w.Tags.IsEmpty() {
return fmt.Errorf("workflow node needs template or tags")
}
if err := workflowNodesValid(w.Subtemplates); err != nil {
return err
}
}
return nil
} Prevention
- Run nuclei -validate on workflow files before scans
- Lint that every workflows: entry has template: or a non-empty tags: list
- Keep subtemplates indented directly under their parent entry
When it happens
Trigger: A workflow YAML whose `workflows:` entry or nested `subtemplates:` entry has neither `template:` nor `tags:`; indentation errors that detach subtemplates from their parent, leaving an effectively empty node; tags given as an empty list.
Common situations: Hand-written workflows missing the template line; copy-paste from docs that drops one line; YAML reformatting that shifts indentation of subtemplates/matchers.
Related errors
- Invalid protocol type: {valueToMap}
- validation failed for these fields
- include directive preprocessing is disabled
- could not parse profile YAML: %w
- could not marshal inline secrets: %w
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/3985a23a818dd25a.
Report an issue: GitHub.