tree-sitter/tree-sitter · error · Error
Grammar must have at least one rule.
Error message
Grammar must have at least one rule.
What it means
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.
Source
Thrown at crates/generate/src/dsl.js:474
let precedences = baseGrammar.precedences;
if (options.precedences) {
if (typeof options.precedences !== "function") {
throw new Error("Grammar's 'precedences' property must be a function");
}
precedences = options.precedences.call(ruleBuilder, ruleBuilder, baseGrammar.precedences);
if (!Array.isArray(precedences)) {
throw new Error("Grammar's precedences must be an array of arrays of rules.");
}
precedences = precedences.map(list => {
if (!Array.isArray(list)) {
throw new Error("Grammar's precedences must be an array of arrays of rules.");
}
return list.map(normalize);
});
}
if (Object.keys(rules).length === 0) {
throw new Error("Grammar must have at least one rule.");
}
return {
grammar: {
name,
inherits,
word,
rules,
extras,
conflicts,
precedences,
externals,
inline,
supertypes,
reserved,
},
};
}View on GitHub (pinned to dff1fd868c)
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.
Example fix
// before
module.exports = grammar({
name: 'empty',
});
// after
module.exports = grammar({
name: 'empty',
rules: {
source_file: $ => repeat(/\w+/),
},
}); Defensive patterns
Strategy: validation
Validate before calling
const options = { name: 'mylang', rules: { /* ... */ } };
if (Object.keys(options.rules ?? {}).length === 0) {
throw new Error('grammar must define at least one rule');
}
module.exports = grammar(options); Prevention
- Start from a template that always includes a source_file rule.
- Run `tree-sitter generate` early so an empty grammar fails immediately.
When it happens
Trigger: `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.
Common situations: Truncated or badly merged grammar.js files. Generating a new grammar from a minimal template before writing any rules.
Related errors
- Invalid alias value ${value}
- Invalid field name '${name}': field names must start with a
- Invalid reserved word set name: ${wordset}
- Undefined symbol
- Invalid rule: ${value}
AI-assisted analysis of tree-sitter/tree-sitter@dff1fd868c (2026-08-16).
Data as JSON: /api/errors/8ce8fba71fba2d48.
Report an issue: GitHub.