github/spec-kit · error · PresetValidationError

Invalid strategy '{strategy}' for script: scripts only suppo

Error message

Invalid strategy '{strategy}' for script: scripts only support {sorted(VALID_SCRIPT_STRATEGIES)}

What it means

A stricter, script-specific strategy check: entries with `type: script` only support {"replace", "wrap"}. Prepending or appending to an executable script does not make sense semantically, so it is rejected even though those strategies are valid for other template types.

Source

Thrown at src/specify_cli/presets/__init__.py:467

            # Validate strategy field (optional, defaults to "replace")
            strategy = tmpl.get("strategy", "replace")
            if not isinstance(strategy, str):
                raise PresetValidationError(
                    f"Invalid strategy value: must be a string, "
                    f"got {type(strategy).__name__}"
                )
            strategy = strategy.lower()
            # Persist normalized value so downstream code sees lowercase
            if "strategy" in tmpl:
                tmpl["strategy"] = strategy
            if strategy not in VALID_PRESET_STRATEGIES:
                raise PresetValidationError(
                    f"Invalid strategy '{strategy}': "
                    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"
                    )

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Set the script template's strategy to "replace" or "wrap".
  2. If you actually need prepend/append behavior, the entry is not a script — reconsider its type or restructure so the script itself composes content.
  3. Omit strategy for scripts wanting full overwrite — default is "replace".

Example fix

# before
  - name: setup
    type: script
    file: scripts/setup.sh
    strategy: append

# after
    strategy: "replace"
Defensive patterns

Strategy: validation

Validate before calling

SCRIPT_OK = {"replace", "wrap"}
for t in yaml.safe_load(open("preset.yml")).get("templates", []):
    if t.get("type") == "script":
        s = str(t.get("strategy", "replace")).lower()
        assert s in SCRIPT_OK, f"script {t.get('name')}: strategy must be replace or wrap, got {s!r}"

Type guard

def script_strategy_ok(template: dict) -> bool:
    return str(template.get("strategy", "replace")).lower() in {"replace", "wrap"}

Try / catch

try:
    manager.install_from_directory(src, version)
except PresetValidationError as e:
    if "for script" in str(e):
        # script templates only accept replace/wrap; fix and retry
        ...

Prevention

When it happens

Trigger: preset.yml contains a template with `type: script` and `strategy: prepend` or `strategy: append`. The general strategy check (VALID_PRESET_STRATEGIES) passes first, then this script-specific check fails.

Common situations: Copying a command template entry (which supports prepend/append for content merging) and changing only its type to script, or assuming one uniform strategy vocabulary across all template types.

Related errors


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