{"record":{"id":"bad88d904487fab7","repo":"air-verse/air","slug":"build-rules-d-s-at-least-one-of-include-dir","errorCode":null,"errorMessage":"build.rules[%d] (%s): at least one of include_dir, include_ext or include_file is required","messagePattern":"build\\.rules\\[(.+?)\\] \\((.+?)\\): at least one of include_dir, include_ext or include_file is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"runner/config.go","lineNumber":180,"sourceCode":"\nfunc (r *cfgRule) delay() time.Duration {\n\tif r.Delay <= 0 {\n\t\treturn 1000 * time.Millisecond\n\t}\n\treturn time.Duration(r.Delay) * time.Millisecond\n}\n\nfunc (c *cfgBuild) normalizeRules(root string) error {\n\tfor i := range c.Rules {\n\t\tr := &c.Rules[i]\n\t\tif r.Name == \"\" {\n\t\t\tr.Name = fmt.Sprintf(\"rule-%d\", i)\n\t\t}\n\t\tif r.Cmd == \"\" {\n\t\t\treturn fmt.Errorf(\"build.rules[%d] (%s): cmd is required\", i, r.Name)\n\t\t}\n\t\tif len(r.IncludeDir) == 0 && len(r.IncludeExt) == 0 && len(r.IncludeFile) == 0 {\n\t\t\treturn fmt.Errorf(\"build.rules[%d] (%s): at least one of include_dir, include_ext or include_file is required\", i, r.Name)\n\t\t}\n\t\tr.includeDirAbs = r.includeDirAbs[:0]\n\t\tfor _, dir := range r.IncludeDir {\n\t\t\tdir = cleanPath(dir)\n\t\t\tif dir == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tabs := filepath.Clean(dir)\n\t\t\tif !filepath.IsAbs(abs) {\n\t\t\t\tabs = filepath.Join(root, abs)\n\t\t\t}\n\t\t\tr.includeDirAbs = append(r.includeDirAbs, filepath.Clean(abs))\n\t\t}\n\t\tr.regexCompiled = r.regexCompiled[:0]\n\t\tfor _, expr := range r.ExcludeRegex {\n\t\t\tre, err := regexp.Compile(expr)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"build.rules[%d] (%s): failed to compile regex %q: %w\", i, r.Name, expr, err)","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/air-verse/air/blob/71ea1dee05248122ed22b5870c7fb20a90d80ddd/runner/config.go#L162-L198","documentation":"normalizeRules requires each `build.rules` entry to declare at least one matcher — include_dir, include_ext, or include_file — otherwise the rule could never match anything and is rejected. The error names the rule index and name.","triggerScenarios":"Loading a `.air.toml` whose `[[build.rules]]` has a cmd but none of include_dir / include_ext / include_file set (all empty arrays or absent).","commonSituations":"Writing a rule with only cmd and name and forgetting the matcher; commenting out the include_ext line; mispelling the key (e.g. include_ex) so it parses as empty.","solutions":["Add at least one matcher, e.g. include_ext = [\".go\"] or include_dir = [\"cmd\"]","Delete the rule if it is not needed","Check for typos in the include_* key names"],"exampleFix":"// before (.air.toml)\n[[build.rules]]\nname = \"tests\"\ncmd = \"go test ./...\"\n// after (.air.toml)\n[[build.rules]]\nname = \"tests\"\ncmd = \"go test ./...\"\ninclude_ext = [\"_test.go\"]","handlingStrategy":"validation","validationCode":"# each rules block must set include_dir, include_ext or include_file\ngrep -A5 '^\\\\[\\\\[build.rules\\\\]\\\\]' .air.toml | grep -Eq 'include_(dir|ext|file)' || echo \"rule missing include_*\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair a rule's cmd with at least one include_* matcher","Watch for typos in include_dir/include_ext/include_file keys","Delete unused rules instead of leaving empty stubs"],"tags":["config","validation","rules","toml"],"backgroundTag":"missing-required-config-field","analyzedSha":"71ea1dee05248122ed22b5870c7fb20a90d80ddd","analyzedAt":"2026-08-31T20:27:54.676Z","schemaVersion":2},"datasetVersion":"2026-09-01T03:17:15.561Z"}