{"record":{"id":"fe2f4607679e90f3","repo":"tree-sitter/tree-sitter","slug":"grammar-s-name-property-must-be-a-string","errorCode":null,"errorMessage":"Grammar's 'name' property must be a string.","messagePattern":"Grammar's 'name' property must be a string\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/generate/src/dsl.js","lineNumber":303,"sourceCode":"\n  const ruleMap = {};\n  for (const key of Object.keys(options.rules)) {\n    ruleMap[key] = true;\n  }\n  for (const key of Object.keys(baseGrammar.rules)) {\n    ruleMap[key] = true;\n  }\n  for (const external of externals) {\n    if (typeof external.name === 'string') {\n      ruleMap[external.name] = true;\n    }\n  }\n\n  const ruleBuilder = RuleBuilder(ruleMap);\n\n  const name = options.name;\n  if (typeof name !== \"string\") {\n    throw new Error(\"Grammar's 'name' property must be a string.\");\n  }\n\n  if (!/^[a-zA-Z_]\\w*$/.test(name)) {\n    throw new Error(\"Grammar's 'name' property must not start with a digit and cannot contain non-word characters.\");\n  }\n\n  if (inherits && typeof inherits !== \"string\") {\n    throw new Error(\"Base grammar's 'name' property must be a string.\");\n  }\n\n  if (inherits && !/^[a-zA-Z_]\\w*$/.test(name)) {\n    throw new Error(\"Base grammar's 'name' property must not start with a digit and cannot contain non-word characters.\");\n  }\n\n  const rules = Object.assign({}, baseGrammar.rules);\n  if (options.rules) {\n    if (typeof options.rules !== \"object\") {\n      throw new Error(\"Grammar's 'rules' property must be an object.\");","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/tree-sitter/tree-sitter/blob/dff1fd868c750dbbae179fcd5c43ce987e4e0528/crates/generate/src/dsl.js#L285-L321","documentation":"Thrown by `grammar({...})` when the `name` option is not a string. The grammar name drives the generated language name and parser symbols (`tree_sitter_<name>`, `TS_PARSER_NAME`), so it is mandatory and must be a string. Omitting `name` entirely (undefined), passing null, or computing it from a non-string source all fail here — this fires before the identifier-format check on the next lines.","triggerScenarios":"`grammar({ rules: {...} })` with no `name` key at all; `name: null`; `name: 123`; a name imported from a module that exported undefined (typo'd import name that didn't fail at load); inheriting grammars where the author assumed the base grammar's name carries over.","commonSituations":"Scaffolding a new grammar from a template and deleting or renaming the name field; grammars that compute `name` from package metadata with a failed lookup; inheriting grammars forgetting that the derived grammar still needs its own `name`.","solutions":["Add a string name, snake_cased: `name: 'my_language'`.","Keep it consistent with the package name: package `tree-sitter-my-language` uses `name: 'my_language'`.","If the name is computed, fall back to a literal default rather than shipping undefined."],"exampleFix":"// before\nmodule.exports = grammar({\n  rules: { source_file: $ => $.expr }\n});\n\n// after\nmodule.exports = grammar({\n  name: 'my_language',\n  rules: { source_file: $ => $.expr }\n});","handlingStrategy":"validation","validationCode":"function validateGrammarOptions(options) {\n  if (typeof options.name !== 'string') {\n    throw new Error(\"Grammar's 'name' must be a snake_case string, e.g. 'my_language'\");\n  }\n}","typeGuard":"const hasValidName = (o) => typeof o.name === 'string';","tryCatchPattern":null,"preventionTips":["Treat `name` as required scaffolding, like package.json's name field.","Derive it once from the package name and reuse: `name: 'my_language'` for `tree-sitter-my-language`.","Add a smoke test that loads grammar.js and asserts `typeof grammarObj.name === 'string'`."],"tags":["tree-sitter","grammar-config","name","required-field"],"backgroundTag":"config-property-wrong-type","analyzedSha":"dff1fd868c750dbbae179fcd5c43ce987e4e0528","analyzedAt":"2026-08-16T22:01:49.632Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}