github/spec-kit · error · ValidationError
Each entry in 'provides.{section}' must be a mapping
Error message
Each entry in 'provides.{section}' must be a mapping What it means
A provides.templates or provides.scripts entry is not a mapping. The shared artifact validator (used for both sections via the 'section'/'singular' parameters) requires every entry to be a dict with at least 'name' and 'file' keys. Templates and scripts are not namespaced by command name, so they follow plain-slug rules, but the container-element shape is mandatory.
Source
Thrown at src/specify_cli/extensions/__init__.py:589
"""Validate provides.templates / provides.scripts entries.
Mirrors the shape/path-safety checks PresetManifest applies to its
non-command templates, minus 'type' (the section name already
distinguishes template vs script) and 'strategy' (extension-provided
artifacts are always 'replace' -- see the forced-replace resolver
behavior for extension layers in presets/__init__.py). A present
'strategy' key is rejected rather than silently ignored, so an author
who copies a preset-style entry gets a clear error instead of a
silently-dropped field. Duplicate names within a section are also
rejected: the resolver returns the first matching entry by name
(``PresetResolver._extension_manifest_declared_template``), so a
later duplicate would be silently unreachable while still being
exposed by ``ExtensionManifest.templates``/``.scripts``.
"""
seen_names: set[str] = set()
for entry in entries:
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}'"View on GitHub (pinned to bf88c9f9a8)
Solutions
- Convert each entry to a mapping with 'name' and 'file': {"name": "plan", "file": "templates/plan.md"}.
- Check the sibling entries in the same section — one bad element fails the whole manifest.
- Re-run extension install and fix the first reported section, then any subsequent ones.
Example fix
// before
"templates": ["templates/plan.md"]
// after
"templates": [{ "name": "plan", "file": "templates/plan.md" }] Defensive patterns
Strategy: validation
Validate before calling
for section in ("templates", "scripts"):
for entry in manifest.get("provides", {}).get(section, []):
if not isinstance(entry, dict):
raise SystemExit(f"provides.{section} entry is not a mapping: {entry!r}") Type guard
def artifact_entries_are_mappings(provides: dict, section: str) -> bool:
return all(isinstance(e, dict) for e in provides.get(section, [])) Try / catch
try:
ExtensionManifest.load(path)
except ValidationError as e:
if "must be a mapping" in str(e):
# convert the bare path/string entry into {"name": ..., "file": ...} and retry
... Prevention
- Every provides.templates/scripts element needs {name, file} object shape.
- Prefer copy-pasting a working entry over typing from memory.
- Add a manifest schema check to extension CI.
When it happens
Trigger: "templates": ["plan.md"] or "scripts": [{"name": "setup", "file": "scripts/x.sh"}, "raw-string"]. Raised in the entries loop of the artifact validator during manifest _validate().
Common situations: Author lists bare file paths instead of {name, file} objects; generated manifests append a path string by mistake; YAML indentation turning a mapping into a scalar.
Related errors
- Invalid runtimes for script '{name}': expected a list of str
- Invalid runtimes {invalid} for script '{name}': must be one
- Manifest must be a YAML mapping, got {type(data).__name__}:
- Missing required field: {field}
- Invalid extension: expected a mapping, got {type(ext).__name
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/572134b11e1d784d.
Report an issue: GitHub.