OpenBMB/ChatDev · error · ConfigError

duration_unit must be one of: {', '.join(valid_units)}

Error message

duration_unit must be one of: {', '.join(valid_units)}

What it means

LoopTimerConfig.from_dict validates the optional 'duration_unit' (default 'seconds') against exactly ['seconds', 'minutes', 'hours']. Unlike input_mode, this check is case-sensitive with no normalization — str() is applied, so the raw spelling must match.

Source

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

        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))
        message = optional_str(mapping, "message", path)
        passthrough = bool(mapping.get("passthrough", False))

        return cls(
            max_duration=max_duration,
            duration_unit=duration_unit,
            reset_on_emit=reset_on_emit,
            message=message,
            passthrough=passthrough,
            path=path,
        )

    def validate(self) -> None:

View on GitHub (pinned to 4fb2db0ea9)

Solutions

  1. Use exactly 'seconds', 'minutes', or 'hours' (lowercase)
  2. Omit duration_unit to keep the 'seconds' default
  3. Normalize user input: unit.strip().lower() before building the dict, and map shorthand like 's'/'sec' to 'seconds'

Example fix

# before
{"max_duration": 5, "duration_unit": "Minutes"}
# after
{"max_duration": 5, "duration_unit": "minutes"}
Defensive patterns

Strategy: type-guard

Validate before calling

UNITS = {'seconds', 'minutes', 'hours'}
u = str(data.get('duration_unit', 'seconds'))
if u not in UNITS:
    data['duration_unit'] = 'seconds'

Type guard

def unit_ok(data: dict) -> bool:
    return str(data.get('duration_unit', 'seconds')) in {'seconds', 'minutes', 'hours'}

Try / catch

try:
    LoopTimerConfig.from_dict(data, path='lt')
except ConfigError as e:
    if 'duration_unit' in e.path:
        data['duration_unit'] = 'seconds'
        LoopTimerConfig.from_dict(data, path='lt')
    else:
        raise

Prevention

When it happens

Trigger: Passing duration_unit: 'Seconds' (capitalized), 's', 'min', 'm', 'day', or 'days'. Only the exact lowercase strings 'seconds', 'minutes', 'hours' pass.

Common situations: Assuming case-insensitivity like other fields in this library; using shorthand units from other config formats; typos.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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