{"record":{"id":"f9fdbc3fbc3c2077","repo":"OpenBMB/ChatDev","slug":"max-duration-must-be-0","errorCode":null,"errorMessage":"max_duration must be > 0","messagePattern":"max_duration must be > 0","errorType":"validation","errorClass":"ConfigError","httpStatus":null,"severity":"error","filePath":"entity/configs/node/loop_timer.py","lineNumber":42,"sourceCode":"    message: Optional[str] = None\n    passthrough: bool = False\n\n    @classmethod\n    def from_dict(\n        cls, data: Mapping[str, Any] | None, *, path: str\n    ) -> \"LoopTimerConfig\":\n        mapping = require_mapping(data or {}, path)\n        max_duration_raw = mapping.get(\"max_duration\", 60.0)\n        try:\n            max_duration = float(max_duration_raw)\n        except (TypeError, ValueError) as exc:  # pragma: no cover - defensive\n            raise ConfigError(\n                \"max_duration must be a number\",\n                extend_path(path, \"max_duration\"),\n            ) from exc\n\n        if max_duration <= 0:\n            raise ConfigError(\n                \"max_duration must be > 0\", extend_path(path, \"max_duration\")\n            )\n\n        duration_unit = str(mapping.get(\"duration_unit\", \"seconds\"))\n        valid_units = [\"seconds\", \"minutes\", \"hours\"]\n        if duration_unit not in valid_units:\n            raise ConfigError(\n                f\"duration_unit must be one of: {', '.join(valid_units)}\",\n                extend_path(path, \"duration_unit\"),\n            )\n\n        reset_on_emit = bool(mapping.get(\"reset_on_emit\", True))\n        message = optional_str(mapping, \"message\", path)\n        passthrough = bool(mapping.get(\"passthrough\", False))\n\n        return cls(\n            max_duration=max_duration,\n            duration_unit=duration_unit,","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/OpenBMB/ChatDev/blob/4fb2db0ea90375ce1059f44fe03ffbd191a7a169/entity/configs/node/loop_timer.py#L24-L60","documentation":"LoopTimerConfig.from_dict rejects a max_duration that coerces to zero or a negative float. A timer that expires immediately or in the past is invalid.","triggerScenarios":"Passing max_duration: 0, negative numbers, or numeric strings like '0'/'-5'. Values in minutes/hours are still stored as raw numbers, so 0.5 seconds passes but 0 does not.","commonSituations":"Attempting to disable a timer with 0; arithmetic deriving duration that underflows to 0; unit confusion (entering 1 meaning 1 minute while intending 60 seconds still passes, but 0 never does).","solutions":["Use a positive duration, e.g. 60.0","Pick the smallest sensible bound rather than 0 if you want a tight timeout","To disable timing, remove the loop_timer node rather than zeroing the value"],"exampleFix":"# before\n{\"max_duration\": 0}\n# after\n{\"max_duration\": 60.0}","handlingStrategy":"validation","validationCode":"d = float(data.get('max_duration', 60.0))\nif d <= 0:\n    data['max_duration'] = 60.0","typeGuard":"def duration_positive(data: dict) -> bool:\n    try:\n        return float(data.get('max_duration', 60.0)) > 0\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    LoopTimerConfig.from_dict(data, path='lt')\nexcept ConfigError as e:\n    if '> 0' in str(e):\n        data['max_duration'] = 60.0\n        LoopTimerConfig.from_dict(data, path='lt')\n    else:\n        raise","preventionTips":["Use small positive bounds instead of 0","Validate derived durations before config build","Remove the timer node to disable timing"],"tags":["config","timer","range","validation","python"],"backgroundTag":"value-out-of-range","analyzedSha":"4fb2db0ea90375ce1059f44fe03ffbd191a7a169","analyzedAt":"2026-08-27T14:35:29.622Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}