{"record":{"id":"d66716a58e377e19","repo":"JuliusBrussee/caveman","slug":"saved-state-must-match-the-configured-workbench-count","errorCode":null,"errorMessage":"Saved state must match the configured workbench count","messagePattern":"Saved state must match the configured workbench count","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/autogen.py","lineNumber":467,"sourceCode":"            except BaseException as error:\n                if first_error is None:\n                    first_error = error\n        if first_error is not None:\n            raise first_error\n\n    async def reset(self):\n        for workbench in self.delegates:\n            await workbench.reset()\n\n    async def save_state(self):\n        if type(self.workbench) is list:\n            return {\"workbenches\": [await workbench.save_state() for workbench in self.delegates]}\n        return await self.workbench.save_state()\n\n    async def load_state(self, state):\n        if type(self.workbench) is list:\n            if len(state.get(\"workbenches\", [])) != len(self.delegates):\n                raise ValueError(\"Saved state must match the configured workbench count\")\n            for workbench, saved in zip(self.delegates, state[\"workbenches\"]):\n                await workbench.load_state(saved)\n        else:\n            await self.workbench.load_state(state)\n\n    def _to_config(self):\n        config = [workbench.dump_component() for workbench in self.delegates] if type(self.workbench) is list else self.workbench.dump_component()\n        return CavemanWorkbenchConfig(workbench=config, scope=self.scope, runtime_key=self.runtime_key)\n\n    @classmethod\n    def _from_config(cls, config):\n        runtime = _runtime(config.runtime_key)\n        workbench = [Workbench.load_component(item) for item in config.workbench] if type(config.workbench) is list else Workbench.load_component(config.workbench)\n        return _loaded(cls(workbench, runtime=runtime, scope=config.scope, runtime_key=config.runtime_key))\n\n\ndef with_caveman_model(model_client, *, runtime, scope, runtime_key=\"default\"):\n    \"\"\"Wrap an existing client; recovery-free unless paired with the workbench.\"\"\"","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/autogen.py#L449-L485","documentation":"When a CavemanWorkbench was constructed with a list of delegate workbenches, load_state expects the saved state to contain a 'workbenches' array whose length equals the number of delegates, restoring each delegate's state positionally. A mismatch raises ValueError instead of silently dropping or misaligning saved states.","triggerScenarios":"Calling load_state with a scalar state (saved from a single-workbench configuration) on a multi-workbench CavemanWorkbench, or with a 'workbenches' list of a different length (delegates added/removed between save and load).","commonSituations":"Persisting AutoGenAgent state, changing the number of delegate workbenches between runs, sharing one saved state file across differently configured agents, or saving from a single workbench and loading into a list-configured wrapper.","solutions":["Re-save state with save_state() from the same CavemanWorkbench configuration (same delegate count) before calling load_state.","If the delegate count intentionally changed, migrate the saved state: reshape it into a 'workbenches' list matching the new count.","If you actually have a single workbench's state, load it into a single-workbench CavemanWorkbench instead.","Wrap load_state in try/except ValueError to detect stale state and fall back to a fresh save_state."],"exampleFix":"// before\nawait wrapper.load_state(saved_single_state)  # saved from 1 workbench, wrapper has 2\n// after\nassert len(saved[\"workbenches\"]) == len(wrapper.delegates)\nawait wrapper.load_state(saved)","handlingStrategy":"try-catch","validationCode":"if isinstance(wrapper.workbench, list) and len(state.get(\"workbenches\", [])) != len(wrapper.delegates):\n    raise ValueError(\"state/delegate count mismatch; re-save state\")","typeGuard":null,"tryCatchPattern":"try:\n    await wrapper.load_state(state)\nexcept ValueError:\n    # stale or mis-shaped state; start fresh\n    fresh = await wrapper.save_state()\n    await wrapper.load_state(fresh)","preventionTips":["Always pair save_state/load_state from the same CavemanWorkbench instance/configuration.","Version-tag persisted state with the delegate count so migrations are explicit.","Wrap load_state in try/except ValueError with a fallback to fresh state.","Add a count assertion before loading when restoring from checkpoints."],"tags":["python","autogen","state","workbench","value-error"],"backgroundTag":"shape-mismatch","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}