projectdiscovery/nuclei · error

matcher %s has unexpected fields: %s

Error message

matcher %s has unexpected fields: %s

What it means

Template validation error from Matcher.Validate -> checkFields (pkg/operators/matchers/validate.go:81). The matcher is marshaled to a map, and every YAML key is mapped back to a struct field via getFieldNameFromYamlTag; keys not in the expected field set for the matcher's type (validate.go:35-51, e.g. word: Words/Part/Encoding/CaseInsensitive + common fields) are reported as unexpected, comma-separated. This catches fields misplaced under the wrong matcher type.

Source

Thrown at pkg/operators/matchers/validate.go:81

		}
	}
	return nil
}

func checkFields(m *Matcher, matcherMap map[string]interface{}, expectedFields ...string) error {
	var foundUnexpectedFields []string
	for marshaledFieldName := range matcherMap {
		// revert back the marshaled name to the original field
		structFieldName, err := getFieldNameFromYamlTag(marshaledFieldName, *m)
		if err != nil {
			return err
		}
		if !sliceutil.Contains(expectedFields, structFieldName) {
			foundUnexpectedFields = append(foundUnexpectedFields, structFieldName)
		}
	}
	if len(foundUnexpectedFields) > 0 {
		return fmt.Errorf("matcher %s has unexpected fields: %s", m.matcherType, strings.Join(foundUnexpectedFields, ","))
	}
	return nil
}

func getFieldNameFromYamlTag(tagName string, object interface{}) (string, error) {
	reflectType := reflect.TypeOf(object)
	if reflectType.Kind() != reflect.Struct {
		return "", errors.New("the object must be a struct")
	}
	for idx := 0; idx < reflectType.NumField(); idx++ {
		field := reflectType.Field(idx)
		tagParts := strings.Split(field.Tag.Get("yaml"), ",")
		if len(tagParts) > 0 && tagParts[0] == tagName {
			return field.Name, nil
		}
	}
	return "", fmt.Errorf("field %s not found", tagName)
}

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Keep only fields valid for the matcher type (word: words/part/encoding/case-insensitive; regex: regex/part/...; status: status; size: size; dsl: dsl; binary: binary/part/encoding)
  2. Split mixed concerns into multiple matchers joined by condition instead of one matcher with foreign fields
  3. Validate with `nuclei -validate -t template.yaml` which surfaces the full unexpected-field list
  4. When generating templates programmatically, build typed structs instead of loose maps

Example fix

# before
matchers:
  - type: status
    status:
      - 200
    words:
      - 'ok'
# after
matchers:
  - type: status
    status:
      - 200
  - type: word
    words:
      - 'ok'
Defensive patterns

Strategy: validation

Validate before calling

allowed := map[string][]string{
	"word": {"words", "part", "encoding", "case-insensitive", "condition", "match-all", "name", "internal"},
	"status": {"status", "part"},
	"size": {"size", "part"},
	"regex": {"regex", "part", "encoding", "case-insensitive", "condition"},
	"binary": {"binary", "part", "encoding"},
	"dsl": {"dsl", "condition"},
	"xpath": {"xpath", "part"},
}
// reject matcher maps containing keys outside allowed[type]

Type guard

func matcherKeysValid(t string, keys []string) bool {
	set := allowed[t]
	for _, k := range keys { if !contains(set, k) { return false } }
	return true
}

Try / catch

if err := m.CompileMatchers(); err != nil {
	if ve, ok := m.Validate(); ok == false && strings.Contains(ve.Error(), "unexpected fields") {
		// list allowed fields for the matcher type in the error output
	}
}

Prevention

When it happens

Trigger: E.g. `type: status` with a `words:` list, `type: word` with `regex:` entries, or any key the Matcher struct does define but that type does not accept (also honors unknown keys that fail tag mapping). Note the loop returns early on a totally unknown key via getFieldNameFromYamlTag's error path.

Common situations: Copy-paste between matchers of different types without pruning the old field; tutorials mixing matcher fields; refactors that change type but leave stale keys; YAML anchors merging extra keys into a matcher.

Related errors


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