{"record":{"id":"cda4b09596194590","repo":"github/spec-kit","slug":"invalid-template-name-tmpl-name-must-be-lo","errorCode":null,"errorMessage":"Invalid template name '{tmpl['name']}': must be lowercase alphanumeric with hyphens only","messagePattern":"Invalid template name '(.+?)': must be lowercase alphanumeric with hyphens only","errorType":"validation","errorClass":"PresetValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/presets/__init__.py","lineNumber":482,"sourceCode":"                    f\"must be one of {sorted(VALID_PRESET_STRATEGIES)}\"\n                )\n            if tmpl[\"type\"] == \"script\" and strategy not in VALID_SCRIPT_STRATEGIES:\n                raise PresetValidationError(\n                    f\"Invalid strategy '{strategy}' for script: \"\n                    f\"scripts only support {sorted(VALID_SCRIPT_STRATEGIES)}\"\n                )\n\n            # Validate template name format\n            if tmpl[\"type\"] == \"command\":\n                # Commands use dot notation (e.g. speckit.specify)\n                if not re.match(r'^[a-z0-9.-]+$', tmpl[\"name\"]):\n                    raise PresetValidationError(\n                        f\"Invalid command name '{tmpl['name']}': \"\n                        \"must be lowercase alphanumeric with hyphens and dots only\"\n                    )\n            else:\n                if not re.match(r'^[a-z0-9-]+$', tmpl[\"name\"]):\n                    raise PresetValidationError(\n                        f\"Invalid template name '{tmpl['name']}': \"\n                        \"must be lowercase alphanumeric with hyphens only\"\n                    )\n\n    @property\n    def id(self) -> str:\n        \"\"\"Get preset ID.\"\"\"\n        return self.data[\"preset\"][\"id\"]\n\n    @property\n    def name(self) -> str:\n        \"\"\"Get preset name.\"\"\"\n        return self.data[\"preset\"][\"name\"]\n\n    @property\n    def version(self) -> str:\n        \"\"\"Get preset version.\"\"\"\n        return self.data[\"preset\"][\"version\"]","sourceCodeStart":464,"sourceCodeEnd":500,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/presets/__init__.py#L464-L500","documentation":"Template entries whose type is not `command` (e.g. context, script, or other file templates) must have a `name` matching ^[a-z0-9-]+$ — lowercase letters, digits, and hyphens only, no dots. Dots are reserved for command dot-notation, so a dot in any other template type is rejected.","triggerScenarios":"A non-command template in preset.yml with a name like `config.json`, `my.template`, `Setup_Script`, or any uppercase/underscore/space character. Runs during PresetManifest validation at install.","commonSituations":"Naming a template after the file it produces (e.g. `eslint.config`), which incorrectly includes a dot; or reusing a command-style dotted name for a context/script template.","solutions":["Rename the template to lowercase kebab-case without dots: `config-json`, `setup-script`.","Remember the `file` field carries the actual output filename (which may contain dots) — only `name` is restricted.","If the name is a command with dot notation, its `type` must be `command`."],"exampleFix":"# before\n  - name: eslint.config\n    type: context\n    file: eslint.config.js\n\n# after\n  - name: eslint-config\n    type: context\n    file: eslint.config.js","handlingStrategy":"validation","validationCode":"import re\nfor t in yaml.safe_load(open(\"preset.yml\")).get(\"templates\", []):\n    if t.get(\"type\") != \"command\":\n        assert re.fullmatch(r\"[a-z0-9-]+\", t[\"name\"]), f\"bad template name {t['name']!r} (no dots)\"","typeGuard":"import re\ndef is_valid_template_name(name: str) -> bool:\n    return bool(re.fullmatch(r\"[a-z0-9-]+\", name))","tryCatchPattern":"try:\n    manager.install_from_directory(src, version)\nexcept PresetValidationError as e:\n    if \"Invalid template name\" in str(e):\n        # dots are only allowed for type: command; rename to kebab-case\n        ...","preventionTips":["Non-command template names: lowercase kebab-case, no dots — put the real filename in the file field.","Dots in a name signal it should be type: command."],"tags":["preset","validation","naming"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}