github/spec-kit · error · PresetValidationError
Invalid template name '{tmpl['name']}': must be lowercase al
Error message
Invalid template name '{tmpl['name']}': must be lowercase alphanumeric with hyphens only What it means
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.
Source
Thrown at src/specify_cli/presets/__init__.py:482
f"must be one of {sorted(VALID_PRESET_STRATEGIES)}"
)
if tmpl["type"] == "script" and strategy not in VALID_SCRIPT_STRATEGIES:
raise PresetValidationError(
f"Invalid strategy '{strategy}' for script: "
f"scripts only support {sorted(VALID_SCRIPT_STRATEGIES)}"
)
# Validate template name format
if tmpl["type"] == "command":
# Commands use dot notation (e.g. speckit.specify)
if not re.match(r'^[a-z0-9.-]+$', tmpl["name"]):
raise PresetValidationError(
f"Invalid command name '{tmpl['name']}': "
"must be lowercase alphanumeric with hyphens and dots only"
)
else:
if not re.match(r'^[a-z0-9-]+$', tmpl["name"]):
raise PresetValidationError(
f"Invalid template name '{tmpl['name']}': "
"must be lowercase alphanumeric with hyphens only"
)
@property
def id(self) -> str:
"""Get preset ID."""
return self.data["preset"]["id"]
@property
def name(self) -> str:
"""Get preset name."""
return self.data["preset"]["name"]
@property
def version(self) -> str:
"""Get preset version."""
return self.data["preset"]["version"]View on GitHub (pinned to bf88c9f9a8)
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`.
Example fix
# before
- name: eslint.config
type: context
file: eslint.config.js
# after
- name: eslint-config
type: context
file: eslint.config.js Defensive patterns
Strategy: validation
Validate before calling
import re
for t in yaml.safe_load(open("preset.yml")).get("templates", []):
if t.get("type") != "command":
assert re.fullmatch(r"[a-z0-9-]+", t["name"]), f"bad template name {t['name']!r} (no dots)" Type guard
import re
def is_valid_template_name(name: str) -> bool:
return bool(re.fullmatch(r"[a-z0-9-]+", name)) Try / catch
try:
manager.install_from_directory(src, version)
except PresetValidationError as e:
if "Invalid template name" in str(e):
# dots are only allowed for type: command; rename to kebab-case
... Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid command name '{tmpl['name']}': must be lowercase alp
- Invalid extension ID '{ext['id']}': must be lowercase alphan
- Invalid strategy value: must be a string, got {type(strategy
- Invalid strategy '{strategy}': must be one of {sorted(VALID_
- Invalid strategy '{strategy}' for script: scripts only suppo
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/cda4b09596194590.
Report an issue: GitHub.