github/spec-kit · error · ValidationError
Duplicate {singular} name '{name}' in 'provides.{section}'
Error message
Duplicate {singular} name '{name}' in 'provides.{section}' What it means
Two entries in the same provides.templates or provides.scripts section declared the same 'name'. Duplicates are rejected because the preset resolver returns the first matching entry by name (PresetResolver._extension_manifest_declared_template), so a later duplicate would be silently unreachable while still being listed by ExtensionManifest.templates/.scripts — an invisible correctness trap, hence a hard error.
Source
Thrown at src/specify_cli/extensions/__init__.py:606
if not isinstance(entry, dict):
raise ValidationError(
f"Each entry in 'provides.{section}' must be a mapping"
)
if "name" not in entry or "file" not in entry:
raise ValidationError(f"{singular.capitalize()} missing 'name' or 'file'")
name = entry["name"]
if not isinstance(name, str):
raise ValidationError(
f"Invalid {singular} name: expected a string, got {type(name).__name__}"
)
if not VALID_EXTENSION_ARTIFACT_NAME_PATTERN.match(name):
raise ValidationError(
f"Invalid {singular} name '{name}': "
"must be lowercase alphanumeric with hyphens only"
)
if name in seen_names:
raise ValidationError(
f"Duplicate {singular} name '{name}' in 'provides.{section}'"
)
seen_names.add(name)
file_value = entry["file"]
reason = relative_extension_path_violation(file_value)
if reason:
label = repr(file_value) if isinstance(file_value, str) else f"for {singular} '{name}'"
raise ValidationError(f"Invalid {singular} 'file' {label}: {reason}")
if "description" in entry and not isinstance(entry["description"], str):
raise ValidationError(
f"Invalid {singular} description for '{name}': expected a string"
)
if "strategy" in entry:
raise ValidationError(
f"Invalid {singular} entry '{name}': 'strategy' is not authorable for "View on GitHub (pinned to bf88c9f9a8)
Solutions
- Give each artifact a unique name within its section, e.g. 'plan' and 'plan-extended'.
- If the duplicate was accidental, delete one of the entries.
- If you are merging manifests, de-duplicate by name and keep the intended 'file'.
Example fix
// before
"templates": [
{ "name": "plan", "file": "templates/plan.md" },
{ "name": "plan", "file": "templates/plan-v2.md" }
]
// after
"templates": [
{ "name": "plan", "file": "templates/plan.md" },
{ "name": "plan-v2", "file": "templates/plan-v2.md" }
] Defensive patterns
Strategy: validation
Validate before calling
for section in ("templates", "scripts"):
names = [e.get("name") for e in manifest.get("provides", {}).get(section, []) if isinstance(e, dict)]
dupes = {n for n in names if names.count(n) > 1}
assert not dupes, f"duplicate {section} names: {dupes}" Type guard
def artifact_names_unique(provides: dict, section: str) -> bool:
names = [e.get("name") for e in provides.get(section, []) if isinstance(e, dict)]
return len(names) == len(set(names)) Try / catch
try:
ExtensionManifest.load(path)
except ValidationError as e:
if "Duplicate" in str(e):
# rename or delete the later duplicate entry and retry
... Prevention
- After merging manifests, de-duplicate artifact names per section.
- Remember the resolver only ever uses the first name match.
- Add a duplicate-name assertion to manifest CI.
When it happens
Trigger: "templates": [{"name": "plan", "file": "a.md"}, {"name": "plan", "file": "b.md"}]. The seen_names set in the artifact validator triggers on the second entry during manifest validation.
Common situations: Merging two extensions' manifests by concatenation; copy-pasting an entry and forgetting to change the name; renaming one file but not its manifest name.
Related errors
- Manifest must be a YAML mapping, got {type(data).__name__}:
- Missing required field: {field}
- Invalid extension: expected a mapping, got {type(ext).__name
- Missing extension.{field}
- Invalid extension.{field}: expected a string, got {type(ext[
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/bf60e9a309f61d2a.
Report an issue: GitHub.