OpenBMB/ChatDev · error · ConfigError

max_duration must be a number

Error message

max_duration must be a number

What it means

LoopTimerConfig.from_dict could not coerce 'max_duration' (default 60.0) to float. Non-numeric strings, None, or sequences raise TypeError/ValueError and surface as this config error.

Source

Thrown at entity/configs/node/loop_timer.py:36

class LoopTimerConfig(BaseConfig):
    """Configuration schema for the loop timer node type."""

    max_duration: float = 60.0
    duration_unit: str = "seconds"
    reset_on_emit: bool = True
    message: Optional[str] = None
    passthrough: bool = False

    @classmethod
    def from_dict(
        cls, data: Mapping[str, Any] | None, *, path: str
    ) -> "LoopTimerConfig":
        mapping = require_mapping(data or {}, path)
        max_duration_raw = mapping.get("max_duration", 60.0)
        try:
            max_duration = float(max_duration_raw)
        except (TypeError, ValueError) as exc:  # pragma: no cover - defensive
            raise ConfigError(
                "max_duration must be a number",
                extend_path(path, "max_duration"),
            ) from exc

        if max_duration <= 0:
            raise ConfigError(
                "max_duration must be > 0", extend_path(path, "max_duration")
            )

        duration_unit = str(mapping.get("duration_unit", "seconds"))
        valid_units = ["seconds", "minutes", "hours"]
        if duration_unit not in valid_units:
            raise ConfigError(
                f"duration_unit must be one of: {', '.join(valid_units)}",
                extend_path(path, "duration_unit"),
            )

        reset_on_emit = bool(mapping.get("reset_on_emit", True))

View on GitHub (pinned to 4fb2db0ea9)

Solutions

  1. Set max_duration to a number (float/int) in the config's base unit
  2. Move units into duration_unit ('seconds'/'minutes'/'hours'), not into the number
  3. Sanitize env-derived values: float(value or 60.0)

Example fix

# before
{"max_duration": "30s"}
# after
{"max_duration": 30, "duration_unit": "seconds"}
Defensive patterns

Strategy: validation

Validate before calling

raw = data.get('max_duration', 60.0)
try:
    data['max_duration'] = float(raw)
except (TypeError, ValueError):
    data['max_duration'] = 60.0

Type guard

def duration_coercible(data: dict) -> bool:
    try:
        float(data.get('max_duration', 60.0))
        return True
    except (TypeError, ValueError):
        return False

Try / catch

try:
    LoopTimerConfig.from_dict(data, path='lt')
except ConfigError as e:
    if 'must be a number' in str(e):
        data['max_duration'] = 60.0
        LoopTimerConfig.from_dict(data, path='lt')
    else:
        raise

Prevention

When it happens

Trigger: Passing max_duration as 'one minute', null, or a list. Numeric strings like '30' parse fine via float(), so only genuinely non-numeric values fail.

Common situations: Blank YAML values becoming None; unit-aware strings ('30s') pasted where a bare number is expected; env-var defaults that are empty strings.

Related errors


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