OpenBMB/ChatDev · error · ConfigError

tool_sources must be a list of strings

Error message

tool_sources must be a list of strings

What it means

Raised when tooling 'tool_sources' is a list containing a non-string entry. Each entry is validated item-by-item and the ConfigError path points at the exact offending index (tool_sources[N]).

Source

Thrown at entity/configs/node/tooling.py:396

        else:
            raise ConfigError("timeout must be numeric", extend_path(path, "timeout"))

        cache_ttl_value = mapping.get("cache_ttl", 0.0)
        if cache_ttl_value is None:
            cache_ttl = 0.0
        elif isinstance(cache_ttl_value, (int, float)):
            cache_ttl = float(cache_ttl_value)
        else:
            raise ConfigError("cache_ttl must be numeric", extend_path(path, "cache_ttl"))

        tool_sources_raw = mapping.get("tool_sources")
        tool_sources: List[str] | None = None
        if tool_sources_raw is not None:
            entries = ensure_list(tool_sources_raw)
            normalized: List[str] = []
            for idx, entry in enumerate(entries):
                if not isinstance(entry, str):
                    raise ConfigError(
                        "tool_sources must be a list of strings",
                        extend_path(path, f"tool_sources[{idx}]"),
                    )
                value = entry.strip()
                if value:
                    normalized.append(value)
            tool_sources = normalized
        else:
            tool_sources = ["mcp_tools"]

        return cls(
            server=server,
            headers=headers,
            timeout=timeout,
            cache_ttl=cache_ttl,
            tool_sources=tool_sources,
            path=path,
        )

View on GitHub (pinned to 4fb2db0ea9)

Solutions

  1. Ensure every element of tool_sources is a plain string
  2. Replace object entries like {"name": "x"} with "x"
  3. Strip/convert values before passing the mapping, e.g. [str(s) for s in tool_sources] if all entries are scalar

Example fix

# before
tool_sources:
  - web
  - {name: db}
# after
tool_sources:
  - web
  - db
Defensive patterns

Strategy: type-guard

Validate before calling

ts = cfg.get("tool_sources")
if ts is not None:
    assert isinstance(ts, list) and all(isinstance(x, str) for x in ts)

Type guard

def valid_tool_sources(v) -> bool:
    return v is None or (isinstance(v, list) and all(isinstance(x, str) for x in v))

Try / catch

except ConfigError as e:
    if "tool_sources" in str(e):
        cfg["tool_sources"] = [str(s) for s in cfg["tool_sources"] if isinstance(s, (str, int))]

Prevention

When it happens

Trigger: tool_sources: ["web", 3] or ["web", null] in the config mapping; a YAML list mixing scalars; a list of dicts describing tools.

Common situations: Refactoring tool source names from objects to plain strings and leaving one dict behind; JSON merging that injects numbers; empty-string entries are silently dropped, so only type errors trigger this.

Related errors


AI-assisted analysis of OpenBMB/ChatDev@4fb2db0ea9 (2026-08-27). Data as JSON: /api/errors/d3ba790ee457be36. Report an issue: GitHub.