{"record":{"id":"90c6ebe469d44431","repo":"OpenBMB/ChatDev","slug":"node-node-id-missing-loop-counter-configuration","errorCode":null,"errorMessage":"Node {node.id} missing loop_counter configuration","messagePattern":"Node (.+?) missing loop_counter configuration","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"runtime/node/executor/loop_counter_executor.py","lineNumber":19,"sourceCode":"\"\"\"Loop counter guard node executor.\"\"\"\n\nfrom typing import List, Dict, Any\n\nfrom entity.configs import Node\nfrom entity.configs.node.loop_counter import LoopCounterConfig\nfrom entity.messages import Message, MessageRole\nfrom runtime.node.executor.base import NodeExecutor\n\n\nclass LoopCounterNodeExecutor(NodeExecutor):\n    \"\"\"Track loop iterations and emit output only after hitting the limit.\"\"\"\n\n    STATE_KEY = \"loop_counter\"\n\n    def execute(self, node: Node, inputs: List[Message]) -> List[Message]:\n        config = node.as_config(LoopCounterConfig)\n        if config is None:\n            raise ValueError(f\"Node {node.id} missing loop_counter configuration\")\n\n        state = self._get_state()\n        counter = state.setdefault(node.id, {\"count\": 0})\n        counter[\"count\"] += 1\n        count = counter[\"count\"]\n\n        if count < config.max_iterations:\n            self.log_manager.debug(\n                f\"LoopCounter {node.id}: iteration {count}/{config.max_iterations} (suppress downstream)\"\n            )\n            return []\n\n        if config.reset_on_emit:\n            counter[\"count\"] = 0\n\n        content = config.message or f\"Loop limit reached ({config.max_iterations})\"\n        metadata = {\n            \"loop_counter\": {","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/OpenBMB/ChatDev/blob/4fb2db0ea90375ce1059f44fe03ffbd191a7a169/runtime/node/executor/loop_counter_executor.py#L1-L37","documentation":"LoopCounterNodeExecutor requires a LoopCounterConfig on the node; as_config returned None so it cannot read counter settings (thresholds, reset behavior).","triggerScenarios":"Executing a node assumed to be a loop counter without a LoopCounterConfig payload in its config dict.","commonSituations":"Workflow JSON missing the config block; config keys renamed between runtime versions; constructing loop-counter nodes manually in tests.","solutions":["Attach a LoopCounterConfig to the node's config","Validate all nodes have configs matching their type before running the workflow","Re-serialize workflows with current tooling after upgrades"],"exampleFix":"# before\nNode(id='c1', node_type='loop_counter')\n\n# after\nNode(id='c1', node_type='loop_counter', config={'max_iterations': 5})","handlingStrategy":"validation","validationCode":"assert node.as_config(LoopCounterConfig) is not None","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate config presence per node type at graph load"],"tags":["loop-counter","missing-config"],"backgroundTag":"missing-node-config","analyzedSha":"4fb2db0ea90375ce1059f44fe03ffbd191a7a169","analyzedAt":"2026-08-27T14:35:29.622Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}