projectdiscovery/nuclei · error

unresolved DSL placeholder %q is not inside a string literal

Error message

unresolved DSL placeholder %q is not inside a string literal

What it means

Runtime resolution error from findDSLStringMarkers (pkg/operators/matchers/dsl_string_markers.go:93). For each expression found by expressions.FindExpressions (markers whose variables are absent from the data map), every literal occurrence of "{{expr}}" in the DSL source must lie fully inside a quoted string literal span. The first occurrence found outside string literals returns this error naming the placeholder.

Source

Thrown at pkg/operators/matchers/dsl_string_markers.go:93

	for _, expr := range expressions.FindExpressions(expression, "{{", "}}", data) {
		if _, ok := seenComplex[expr]; ok {
			continue
		}
		seenComplex[expr] = struct{}{}

		marker := "{{" + expr + "}}"
		for start := 0; ; {
			index := strings.Index(expression[start:], marker)
			if index < 0 {
				break
			}

			index += start
			end := index + len(marker)

			stringSpan, ok := stringLiteralForSpan(stringSpans, index, end)
			if !ok {
				return nil, fmt.Errorf("unresolved DSL placeholder %q is not inside a string literal", expr)
			}

			got := dslStringMarker{start: index, end: end, expr: expr, quote: stringSpan.quote}
			markers = append(markers, got)
			occupiedSpans = append(occupiedSpans, textSpan{start: index, end: end})
			start = end
		}
	}

	for _, match := range dslStringMarkerRegex.FindAllStringSubmatchIndex(expression, -1) {
		if len(match) < 4 || markerWithinSpans(match[0], match[1], occupiedSpans) {
			continue
		}

		stringSpan, ok := stringLiteralForSpan(stringSpans, match[0], match[1])
		if !ok {
			return nil, fmt.Errorf("unresolved DSL placeholder %q is not inside a string literal", expression[match[2]:match[3]])
		}

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Wrap the placeholder in quotes so it becomes a string literal: `"'{{needle}}'"`
  2. Correct or register the variable so it resolves before evaluation (e.g. ensure the generator defines it, or the referenced extractor ran)
  3. Provide a default via `{{var || 'fallback'}}`-style preprocessing upstream, or restructure to compare in a word matcher
  4. Enable -v logging to inspect which placeholder stays unresolved

Example fix

# before
dsl:
  - 'contains(body, {{needle}})'
# after
dsl:
  - "contains(body, '{{needle}}')"
Defensive patterns

Strategy: validation

Validate before calling

// ensure every identifier referenced in the dsl exists in data, else require quoting
for _, v := range govaluateVars(dslExpr) {
	if _, ok := data[v]; !ok && !strings.Contains(dslExpr, "'{{"+v+"}}'") {
		return fmt.Errorf("variable %q unresolved and placeholder unquoted", v)
	}
}

Type guard

func markerInsideLiteral(expr, marker string, spans []stringLiteralSpan) bool { /* replicate stringLiteralForSpan check */ return true }

Try / catch

if err := m.Match(data); err != nil && strings.Contains(err.Error(), "not inside a string literal") {
	// quote the named placeholder and retest locally before redeploying
}

Prevention

When it happens

Trigger: An unresolved {{placeholder}} used as a bare govaluate operand — e.g. `status_code == {{code}}` or `contains(body, {{needle}})` — where the variable is not in the evaluation data, so the marker survives substitution and is then detected outside quotes.

Common situations: Generator/fuzz variables referenced as numbers or identifiers instead of quoted strings in DSL matchers; typos in variable names that never resolve; using an extractor name that produced no value for this request.

Related errors


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