github/spec-kit · error · PresetValidationError

Invalid strategy value: must be a string, got {type(strategy

Error message

Invalid strategy value: must be a string, got {type(strategy).__name__}

What it means

Raised while validating a template entry in preset.yml: the optional 'strategy' field on a template must be a YAML string (e.g. replace, prepend, append, wrap), but some other YAML type was found. The validator reports the offending Python type name so you can see whether YAML parsed your value as an int, bool, list, or dict. It is thrown before any strategy comparison, so nothing is installed.

Source

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

            if tmpl["type"] not in VALID_PRESET_TEMPLATE_TYPES:
                raise PresetValidationError(
                    f"Invalid template type '{tmpl['type']}': "
                    f"must be one of {sorted(VALID_PRESET_TEMPLATE_TYPES)}"
                )

            # Validate file path safety: must be relative, no parent traversal
            file_path = tmpl["file"]
            normalized = os.path.normpath(file_path)
            if os.path.isabs(normalized) or normalized.startswith(".."):
                raise PresetValidationError(
                    f"Invalid template file path '{file_path}': "
                    "must be a relative path within the preset directory"
                )

            # 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)}"
                )

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Open preset.yml and set the template's strategy to a quoted string: `strategy: "replace"` (valid values: append, prepend, replace, wrap).
  2. If the strategy is optional for your case, delete the key entirely — it defaults to "replace".
  3. If you generate preset.yml with yaml.safe_dump, ensure you pass a str value, not a bool/int/list.

Example fix

# before (preset.yml)
templates:
  - name: build
    type: script
    file: scripts/build.sh
    strategy: true   # YAML bool

# after
    strategy: "replace"
Defensive patterns

Strategy: type-guard

Validate before calling

import yaml

m = yaml.safe_load(open("preset.yml"))
for t in m.get("templates", []):
    s = t.get("strategy", "replace")
    assert isinstance(s, str), f"template {t.get('name')}: strategy must be a string, got {type(s).__name__}"

Type guard

def is_valid_strategy_type(value) -> bool:
    return isinstance(value, str)

Try / catch

try:
    manager.install_from_directory(src, version)
except PresetValidationError as e:
    if "Invalid strategy value" in str(e):
        # fix preset.yml strategy field, re-run
        ...

Prevention

When it happens

Trigger: A template entry in preset.yml whose 'strategy' key is a non-string YAML scalar or collection, e.g. `strategy: [replace]` (list), `strategy: {name: replace}` (dict), `strategy: 10` (int), or `strategy: true` (bool). YAML booleans are a frequent trap: an unquoted value is parsed by YAML into a bool/int, not a string.

Common situations: Hand-editing preset.yml and writing `strategy: yes`/`strategy: no` (parsed as bool), copying a JSON-style array from docs, or generating the manifest programmatically and inserting a Python object instead of a str.

Related errors


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