github/spec-kit · error · BundlerError

Invalid install_policy '{value}' (must be one of {[p.value f

Error message

Invalid install_policy '{value}' (must be one of {[p.value for p in cls]}).

What it means

Raised by InstallPolicy.parse() when the supplied install_policy string does not equal any enum value ('install-allowed' or 'discovery-only'). Parsing is exact-match on the stripped string, so case variants, abbreviations, or legacy values are rejected. This guards catalog configuration from silently falling back to a permissive install policy.

Source

Thrown at src/specify_cli/bundler/models/catalog.py:35

CONFIG_FILENAME = "bundle-catalogs.yml"
# Supported bundle-catalogs.yml schema (major version). Both readers of the
# file — this module's _merge_config and commands_impl/catalog_config._read —
# reject an unsupported major version so a file written by a newer/incompatible
# Spec Kit fails fast instead of being parsed under the wrong assumptions.
CONFIG_SCHEMA_VERSION = "1.0"


class InstallPolicy(str, Enum):
    INSTALL_ALLOWED = "install-allowed"
    DISCOVERY_ONLY = "discovery-only"

    @classmethod
    def parse(cls, value: Any) -> "InstallPolicy":
        text = str(value or "").strip()
        for policy in cls:
            if policy.value == text:
                return policy
        raise BundlerError(
            f"Invalid install_policy '{value}' "
            f"(must be one of {[p.value for p in cls]})."
        )


class Scope(str, Enum):
    PROJECT = "project"
    USER = "user"
    BUILTIN = "built-in"


# Built-in default stack (used when no project/user config overrides it).
BUILTIN_DEFAULT_STACK: tuple[dict[str, Any], ...] = (
    {"id": "default", "url": "builtin://default", "priority": 1,
     "install_policy": InstallPolicy.INSTALL_ALLOWED.value},
    {"id": "community", "url": "builtin://community", "priority": 20,
     "install_policy": InstallPolicy.DISCOVERY_ONLY.value},
)

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Use exactly one of the enum strings: 'install-allowed' or 'discovery-only' (lowercase, hyphenated).
  2. If loading user input, validate against [p.value for p in InstallPolicy] before calling parse/add_source.
  3. Fix the install_policy field in the catalog config file (project .specify catalog config) to a legal value.
  4. For 'unspecified', omit the parameter only if the API you are calling applies its own default — never pass an empty string.

Example fix

# before
add_source(root, "src", url, priority=5, policy="allow-install")  # raises

# after
add_source(root, "src", url, priority=5, policy="install-allowed")
Defensive patterns

Strategy: validation

Validate before calling

from specify_cli.bundler.models.catalog import InstallPolicy

VALID_POLICIES = {p.value for p in InstallPolicy}
if user_policy not in VALID_POLICIES:
    raise ValueError(f"policy must be one of {sorted(VALID_POLICIES)}")
add_source(root, sid, url, priority=10, policy=user_policy)

Type guard

def is_install_policy(value: str) -> bool:
    return isinstance(value, str) and value in {p.value for p in InstallPolicy}

Try / catch

try:
    InstallPolicy.parse(policy)
except BundlerError as e:
    # surface valid options to the user from the error text
    show(f"Invalid policy: {e}")

Prevention

When it happens

Trigger: Calling add_source(..., policy='allow'), policy='Install-Allowed', or policy='' (empty); loading a catalog config file whose install_policy value predates or misspells the current enum; passing None (str(None or '') becomes '' and matches nothing).

Common situations: Hand-edited catalog config files with typos; older config files written against different policy names; scripts passing boolean-ish or abbreviated values; copy-paste from docs of a different tool version.

Related errors


AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14). Data as JSON: /api/errors/082b85b10206aeb1. Report an issue: GitHub.