{"record":{"id":"8ce8fba71fba2d48","repo":"tree-sitter/tree-sitter","slug":"grammar-must-have-at-least-one-rule","errorCode":null,"errorMessage":"Grammar must have at least one rule.","messagePattern":"Grammar must have at least one rule\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/generate/src/dsl.js","lineNumber":474,"sourceCode":"  let precedences = baseGrammar.precedences;\n  if (options.precedences) {\n    if (typeof options.precedences !== \"function\") {\n      throw new Error(\"Grammar's 'precedences' property must be a function\");\n    }\n    precedences = options.precedences.call(ruleBuilder, ruleBuilder, baseGrammar.precedences);\n    if (!Array.isArray(precedences)) {\n      throw new Error(\"Grammar's precedences must be an array of arrays of rules.\");\n    }\n    precedences = precedences.map(list => {\n      if (!Array.isArray(list)) {\n        throw new Error(\"Grammar's precedences must be an array of arrays of rules.\");\n      }\n      return list.map(normalize);\n    });\n  }\n\n  if (Object.keys(rules).length === 0) {\n    throw new Error(\"Grammar must have at least one rule.\");\n  }\n\n  return {\n    grammar: {\n      name,\n      inherits,\n      word,\n      rules,\n      extras,\n      conflicts,\n      precedences,\n      externals,\n      inline,\n      supertypes,\n      reserved,\n    },\n  };\n}","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/tree-sitter/tree-sitter/blob/dff1fd868c750dbbae179fcd5c43ce987e4e0528/crates/generate/src/dsl.js#L456-L492","documentation":"After merging `options.rules` over the base grammar's rules, the DSL requires at least one rule before assembling the grammar object. An empty rules object (or a missing `rules` key in a standalone grammar) leaves nothing to generate a parser from, so this throws right before the grammar is returned.","triggerScenarios":"`module.exports = grammar({ name: 'x' })` with no `rules` key. `rules: {}` after a botched edit. Rules accidentally nested inside another option or defined under the wrong key.","commonSituations":"Truncated or badly merged grammar.js files. Generating a new grammar from a minimal template before writing any rules.","solutions":["Add a `rules` object with at least one named rule, e.g. `source_file: $ => repeat($.thing)`.","Check that `rules:` sits at the top level of the options object.","When inheriting, require the base grammar before `grammar(base, {...})` so its rules merge in."],"exampleFix":"// before\nmodule.exports = grammar({\n  name: 'empty',\n});\n\n// after\nmodule.exports = grammar({\n  name: 'empty',\n  rules: {\n    source_file: $ => repeat(/\\w+/),\n  },\n});","handlingStrategy":"validation","validationCode":"const options = { name: 'mylang', rules: { /* ... */ } };\nif (Object.keys(options.rules ?? {}).length === 0) {\n  throw new Error('grammar must define at least one rule');\n}\nmodule.exports = grammar(options);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Start from a template that always includes a source_file rule.","Run `tree-sitter generate` early so an empty grammar fails immediately."],"tags":["tree-sitter","grammar-dsl","rules","empty-grammar"],"backgroundTag":"empty-grammar-definition","analyzedSha":"dff1fd868c750dbbae179fcd5c43ce987e4e0528","analyzedAt":"2026-08-16T22:01:49.632Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}