bmad-code-org/BMAD-METHOD · error · RenderError

has unsupported default type

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.

Solutions

  1. Change the default in customize.toml to a string (e.g. retries = "3").
  2. If a list is intended, use TOML array syntax (retries = ["a", "b"]).
  3. 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

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


AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13). Data as JSON: /api/errors/0ba67bcc27259588. Report an issue: GitHub.

Appendix: 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] = resolved

View on GitHub (pinned to b70486b9bd)