bmad-code-org/BMAD-METHOD · error · RenderError
{label} has unsupported default type {type(default).__name__
Error message
{label} has unsupported default type {type(default).__name__} What it means
_resolve_customization_value only supports customization defaults that are strings or lists (a list of dicts becomes review layers; a list of scalars becomes a markdown bullet list). If customize.toml declares a default that is an int, float, bool, inline table, or datetime, the renderer refuses it because it cannot render that type into markdown.
Source
Thrown at src/scripts/render_skill.py:189
if layer.get("when"):
section.extend(["", f"Run only when: {layer['when']}"])
section.extend(["", layer["instruction"].strip()])
sections.append("\n".join(section))
return "\n\n".join(sections)
def _resolve_customization_value(value: Any, default: Any, label: str) -> tuple[Any, str]:
if isinstance(default, str):
allow_empty = not default.strip() or label == "customization.workflow.open_spec"
resolved = _require_string(value, label, allow_empty=allow_empty)
return resolved, resolved
if isinstance(default, list):
if default and all(isinstance(item, dict) for item in default):
resolved = _require_review_layers(value, label)
return resolved, _format_review_layers(resolved)
resolved = _require_string_list(value, label)
return resolved, _format_markdown_list(resolved)
raise RenderError(f"{label} has unsupported default type {type(default).__name__}")
def _resolve_replacements(
sources: dict[str, str],
central: dict[str, Any],
customization: dict[str, Any],
defaults: dict[str, Any] | None,
project_root: Path,
) -> tuple[dict[str, str], dict[str, Any]]:
replacements: dict[str, str] = {}
input_values: dict[str, Any] = {}
for content in sources.values():
for match in _SHORT_CONFIG_TOKEN.finditer(content):
token, key = match.group(0), match.group(1)
path, resolved = _resolve_short_config(central, key, project_root)
source = f"config.{path}"
replacements[token] = resolved
input_values[source] = resolvedView on GitHub (pinned to b70486b9bd)
Solutions
- Change the default in customize.toml to a string (e.g. retries = "3").
- If a list is intended, use TOML array syntax (retries = ["a", "b"]).
- Remove the unsupported {workflow.*} token from sources, or restructure the default to a supported type.
Example fix
# before [workflow] retries = 3 # after [workflow] retries = "3"
Defensive patterns
Strategy: type-guard
Validate before calling
import tomllib
from pathlib import Path
def assert_supported_defaults(toml_path: Path) -> None:
data = tomllib.loads(Path(toml_path).read_text(encoding="utf-8"))
def walk(obj, prefix=""):
if isinstance(obj, dict):
for k, v in obj.items():
walk(v, f"{prefix}.{k}" if prefix else k)
elif not isinstance(obj, (str, list)):
raise SystemExit(f"{prefix}: unsupported default type {type(obj).__name__}")
walk(data) Type guard
def is_supported_default(value: object) -> bool:
return isinstance(value, (str, list)) Prevention
- Keep customize.toml defaults as strings or lists only.
- Quote numeric values (retries = "3").
- Lint defaults for unsupported types (int/bool/table/datetime) before rendering.
When it happens
Trigger: customize.toml has [workflow] retries = 3 (int) or flag = true (bool) or a nested [workflow.x] table that is referenced by a {workflow.retries} / {workflow.flag} token. The looked-up default is neither str nor list.
Common situations: Authoring customization with numeric or boolean settings; using a nested table default where a scalar/list was expected.
Related errors
- {label} must be a list of tables
- {item_label} must be a table
- duplicate review layer id `{identifier}`
- {label} must resolve to an absolute path: {resolved}
- missing config value `{key}`
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/0ba67bcc27259588.
Report an issue: GitHub.