github/spec-kit · error · ValidationError
Invalid extension ID '{ext['id']}': must be lowercase alphan
Error message
Invalid extension ID '{ext['id']}': must be lowercase alphanumeric with hyphens only What it means
Thrown by ExtensionManifest validation while parsing an extension's manifest (extension.yml). The extension.id field must match ^[a-z0-9-]+$ — lowercase letters, digits, and hyphens only. Any uppercase letter, underscore, space, or symbol in the ID fails the regex and aborts extension loading with a ValidationError.
Source
Thrown at src/specify_cli/extensions/__init__.py:317
# ``id: 2`` as an int -- and TypeError is not a ValidationError, so it
# escapes every caller that already handles a malformed manifest (see
# list_installed()'s "Corrupted extension" fallback, which catches
# ValidationError only, making one bad extension exit ``specify
# extension list`` with a raw traceback and hide the healthy ones).
# Mirrors the sibling IntegrationDescriptor, which already type-checks
# the same four fields.
for field in ["id", "name", "version", "description"]:
if field not in ext:
raise ValidationError(f"Missing extension.{field}")
if not isinstance(ext[field], str):
raise ValidationError(
f"Invalid extension.{field}: expected a string, "
f"got {type(ext[field]).__name__}"
)
# Validate extension ID format
if not re.match(r"^[a-z0-9-]+$", ext["id"]):
raise ValidationError(
f"Invalid extension ID '{ext['id']}': "
"must be lowercase alphanumeric with hyphens only"
)
# Validate semantic version
try:
pkg_version.Version(ext["version"])
except pkg_version.InvalidVersion:
raise ValidationError(f"Invalid version: {ext['version']}")
# Validate optional category field (free-form string)
if "category" in ext:
if not isinstance(ext["category"], str) or not ext["category"].strip():
raise ValidationError(
"Invalid extension.category: must be a non-empty string"
)
# Validate optional effect fieldView on GitHub (pinned to bf88c9f9a8)
Solutions
- Set extension.id to a lowercase slug, e.g. `id: agent-context`.
- Remove underscores, dots, spaces, and uppercase characters from the id value in extension.yml.
- Rename the extension directory to match the corrected id so the registry stays consistent.
Example fix
# before (extension.yml) id: My_Agent.Context # after id: my-agent-context
Defensive patterns
Strategy: validation
Validate before calling
import re
def valid_extension_id(ext_id: object) -> bool:
return isinstance(ext_id, str) and re.fullmatch(r"[a-z0-9-]+", ext_id) is not None Type guard
def is_valid_extension_id(v: object) -> bool:
return isinstance(v, str) and bool(re.fullmatch(r"[a-z0-9-]+", v)) Try / catch
try:
manifest = ExtensionManifest.load(path)
except ValidationError as e:
print(f"Manifest invalid: {e}") Prevention
- Use a sluglified id (kebab-case) from the start; enforce it in CI with a manifest lint step.
- Derive the id from the extension directory name, which is already constrained to the same charset.
When it happens
Trigger: An extension manifest with `id: My_Extension`, `id: "agent context"`, or `id: ctx.v2` is loaded (via `specify extension add` or at init when bundled extensions install). re.match(r"^[a-z0-9-]+$", ext["id"]) returns None and the error is raised.
Common situations: Authors naming an extension after a GitHub repo with dots (my-ext.repo), copy-pasting a display name into the id field, or converting underscores from a Python package name. Directory name and id mismatch also surfaces here.
Related errors
- Invalid version: {ext['version']}
- Invalid extension.category: must be a non-empty string
- Invalid extension.effect '{ext.get('effect')}': must be one
- Invalid requires: expected a mapping, got {type(requires).__
- Missing requires.speckit_version
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/aee778f038f7e443.
Report an issue: GitHub.