projectdiscovery/nuclei · error

failed to compile expression

Error message

failed to compile expression %q: %w

What it means

Runtime error from expressions.Evaluate (pkg/protocols/common/expressions/expressions.go:66). After simple placeholders are replaced, each remaining marker is treated as a govaluate expression and compiled with dsl.HelperFunctions. A syntactically invalid expression returns this error wrapping the govaluate failure and echoing the ORIGINAL marker text (originalExpression), so callers can pinpoint the bad template fragment.

Solutions

  1. Fix the marker syntax: balanced braces, valid operators, correct DSL helper arity (check `nuclei -dast` docs / dsl function list via `-h` or dsl.FunctionNames)
  2. Pre-test expressions: put them in a `type: dsl` matcher/extractor and run `nuclei -validate -t template.yaml`
  3. Keep complex logic in the DSL section rather than inline markers where possible
  4. Quote string operands: "'{{var}}' + 'x'" rather than raw concatenation

Example fix

# before
- raw:
    - 'GET /{{path++}} HTTP/1.1'
# after
- raw:
    - "GET /{{path}} HTTP/1.1"
Defensive patterns

Strategy: try-catch

Validate before calling

import gvl "github.com/projectdiscovery/govaluate"

for _, expr := range expressions.FindExpressions(payloadOrRaw, "{{", "}}", data) {
	if _, err := gvl.NewEvaluableExpressionWithFunctions(expr, dsl.HelperFunctions); err != nil {
		return fmt.Errorf("marker %q will fail to compile: %w", expr, err)
	}
}

Type guard

func markerExprCompiles(expr string) bool { _, err := govaluate.NewEvaluableExpressionWithFunctions(expr, dsl.HelperFunctions); return err == nil }

Try / catch

out, err := expressions.Evaluate(template, data)
if err != nil && strings.Contains(err.Error(), "failed to compile expression") {
	// log original marker text (it is quoted in the error) and skip that request gracefully
}

Prevention

When it happens

Trigger: A {{...}} marker that is neither a simple variable nor a valid expression — e.g. '{{BaseURL} }' (bad spacing), '{{payload++}}' with stray operators, or a helper call with wrong arity like '{{md5()}}' (md5 expects 1 arg) — encountered while rendering raw requests, payloads, or flow steps.

Common situations: Fuzzing templates building arithmetic/string expressions from generator values; hand-authored markers with typos; DSL function signature drift after nuclei upgrades (renamed/arity-changed helpers); copy-paste of Go code into markers.

Related errors


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

Appendix: source

Thrown at pkg/protocols/common/expressions/expressions.go:66

	// replace simple placeholders (key => value) MarkerOpen + key + MarkerClose and General + key + General to value
	data = replacer.Replace(data, base)

	// expressions can be:
	// - simple: containing base values keys (variables)
	// - complex: containing helper functions [ + variables]
	// literals like {{2+2}} are not considered expressions
	for _, expression := range expressions {
		originalExpression := expression
		// data has already had simple placeholders replaced; keep the same
		// marker shape for output replacement, but never compile this string.
		replacedExpression := replacer.Replace(expression, base)
		expression = replaceStringPlaceholders(expression, base)

		// turns expressions (either helper functions+base values or base values)
		compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expression, dsl.HelperFunctions)
		if err != nil {
			return data, fmt.Errorf("failed to compile expression %q: %w", originalExpression, err)
		}

		result, err := compiled.Evaluate(base)
		if err != nil {
			return data, fmt.Errorf("failed to evaluate expression %q: %w", originalExpression, err)
		}

		replacement := result
		// Preserve unresolved markers only when a helper call would otherwise
		// hide them from downstream validation. Plain expressions such as
		// comparisons should evaluate normally.
		if markers := unresolvedVarMarkers(compiled.Vars(), base); markers != "" {
			usesFunctions := false
			for _, token := range compiled.Tokens() {
				if token.Kind == govaluate.FUNCTION {
					usesFunctions = true
					break
				}

View on GitHub (pinned to 265b3a3dec)