github/spec-kit · error · PresetValidationError
Invalid strategy '{strategy}': must be one of {sorted(VALID_
Error message
Invalid strategy '{strategy}': must be one of {sorted(VALID_PRESET_STRATEGIES)} What it means
The template's 'strategy' string is not one of the supported merge strategies. Valid values are {"replace", "prepend", "append", "wrap"} (compared case-insensitively — the value is lowercased before the check, and the normalized value is written back into the entry). The error message lists the exact accepted set.
Source
Thrown at src/specify_cli/presets/__init__.py:462
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)}"
)
# 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:View on GitHub (pinned to bf88c9f9a8)
Solutions
- Change the template's strategy in preset.yml to one of: replace, prepend, append, wrap.
- If you wanted to fully overwrite the target file, use "replace" (the default — you can omit the key).
- Check the error message's sorted list; it reflects the exact VALID_PRESET_STRATEGIES set for your installed spec-kit version.
Example fix
# before
strategy: merge
# after
strategy: "replace" Defensive patterns
Strategy: validation
Validate before calling
VALID = {"replace", "prepend", "append", "wrap"}
for t in yaml.safe_load(open("preset.yml")).get("templates", []):
s = str(t.get("strategy", "replace")).lower()
if s not in VALID:
raise SystemExit(f"bad strategy {s!r}; allowed: {sorted(VALID)}") Type guard
def is_valid_strategy(value) -> bool:
return isinstance(value, str) and value.lower() in {"replace", "prepend", "append", "wrap"} Try / catch
try:
manager.install_from_directory(src, version)
except PresetValidationError as e:
if "Invalid strategy" in str(e):
print(e) # message lists the exact allowed set for this spec-kit version Prevention
- Remember the four strategies: replace, prepend, append, wrap (case-insensitive).
- Omit strategy when you want the default replace behavior.
- Cross-check against the error message's list, which reflects your installed version.
When it happens
Trigger: Calling preset install (or PresetManifest validation) with a preset.yml template entry like `strategy: merge`, `strategy: overwrite`, or a typo such as `strategy: apend`. Any string outside {append, prepend, replace, wrap} after lowercasing triggers it.
Common situations: Porting a preset from another tool whose strategy vocabulary differs (merge/overwrite), typos, or docs from an older/newer spec-kit version that supports a different strategy set.
Related errors
- Invalid strategy value: must be a string, got {type(strategy
- Invalid strategy '{strategy}' for script: scripts only suppo
- Unknown template composition strategy '{strategy}' in {path}
- Invalid command name '{tmpl['name']}': must be lowercase alp
- Invalid template name '{tmpl['name']}': must be lowercase al
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/821c001ec56472fb.
Report an issue: GitHub.