projectdiscovery/nuclei · error

failed to evaluate expression

Error message

failed to evaluate expression %q: %w

What it means

Runtime error from expressions.Evaluate (pkg/protocols/common/expressions/expressions.go:71). The expression compiled fine (govaluate.NewEvaluableExpressionWithFunctions succeeded) but compiled.Evaluate(base) failed — typically because a referenced variable is missing from the base data map or a helper function errored at runtime (e.g. bad argument type). The error wraps the govaluate failure and names the original expression.

Solutions

  1. Verify every identifier in the expression is present in the data map at evaluation time (generator payload names, extractor names with internal:true, or built-ins like BaseURL)
  2. Check DSL helper arity/types against the docs and sanitize inputs (e.g. to_string before to_number)
  3. Give extractors fallbacks or gate dependent requests with matchers so variables always exist before use
  4. Reproduce with -v and print the base map to see which key is missing

Example fix

# before
- raw:
    - 'X-Token: {{token}}'
  # token never extracted when first request fails
# after
matchers:
  - type: word
    words:
      - 'session'
    internal: true
# (ensure the producing request matched, so {{token}} exists downstream)
Defensive patterns

Strategy: try-catch

Validate before calling

// before evaluating, confirm every identifier the expression uses is present
for _, v := range compiled.Vars() {
	if _, ok := data[v]; !ok {
		return fmt.Errorf("variable %q missing; skip or provide default before evaluation", v)
	}
}

Type guard

func allVarsPresent(expr string, data map[string]interface{}) bool {
	c, err := govaluate.NewEvaluableExpressionWithFunctions(expr, dsl.HelperFunctions)
	if err != nil { return false }
	for _, v := range c.Vars() { if _, ok := data[v]; !ok { return false } }
	return true
}

Try / catch

out, err := expressions.Evaluate(tplText, data)
if err != nil && strings.Contains(err.Error(), "failed to evaluate expression") {
	// missing runtime variable: skip request, or substitute defaults and retry once
}

Prevention

When it happens

Trigger: '{{randstr(1)}}' with wrong arg type, '{{undefined_var}} == 1' where undefined_var is absent from data (this path fires when it compiled as an identifier but no value exists at eval), or a DSL helper whose runtime input is malformed (e.g. to_number('abc')).

Common situations: Flow/fuzz templates referencing extractor outputs that a skipped request never produced; marker variables whose names drift from the generator's payload names (payloads vs payload); helper functions receiving nil after an upstream extraction failure.

Related errors


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

Appendix: source

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

	// - 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
				}
			}
			if usesFunctions && ContainsUnresolvedVariables(fmt.Sprint(result)) == nil {
				replacement = markers
			}
		}

View on GitHub (pinned to 265b3a3dec)