{"record":{"id":"ccd3534c911057e8","repo":"microsoft/qlib","slug":"you-can-trying-to-get-a-state-from-a-dead-environm","errorCode":null,"errorMessage":"You can trying to get a state from a dead environment wrapper.","messagePattern":"You can trying to get a state from a dead environment wrapper\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"qlib/rl/utils/env_wrapper.py","lineNumber":154,"sourceCode":"        self.status: EnvWrapperStatus = cast(EnvWrapperStatus, None)\n\n    @property\n    def action_space(self) -> Space:\n        return self.action_interpreter.action_space\n\n    @property\n    def observation_space(self) -> Space:\n        return self.state_interpreter.observation_space\n\n    def reset(self, **kwargs: Any) -> ObsType:\n        \"\"\"\n        Try to get a state from state queue, and init the simulator with this state.\n        If the queue is exhausted, generate an invalid (nan) observation.\n        \"\"\"\n\n        try:\n            if self.seed_iterator is None:\n                raise RuntimeError(\"You can trying to get a state from a dead environment wrapper.\")\n\n            # TODO: simulator/observation might need seed to prefetch something\n            # as only seed has the ability to do the work beforehands\n\n            # NOTE: though logger is reset here, logs in this function won't work,\n            # because we can't send them outside.\n            # See https://github.com/thu-ml/tianshou/issues/605\n            self.logger.reset()\n\n            if self.seed_iterator is SEED_INTERATOR_MISSING:\n                # no initial state\n                initial_state = None\n                self.simulator = cast(Callable[[], Simulator], self.simulator_fn)()\n            else:\n                initial_state = next(cast(Iterator[InitialStateType], self.seed_iterator))\n                self.simulator = self.simulator_fn(initial_state)\n\n            self.status = EnvWrapperStatus(","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/rl/utils/env_wrapper.py#L136-L172","documentation":"RuntimeError in `EnvWrapper.reset` (qlib/rl/utils/env_wrapper.py:154). The wrapper pulls the next initial state from `seed_iterator`; once the iterator is exhausted it is set to None, marking the wrapper dead (it should be recycled by the vector env). A reset on such a dead wrapper raises immediately instead of silently reusing stale state.","triggerScenarios":"Calling `env_wrapper.reset()` after its seed iterator raised StopIteration (i.e., after the finite seed set was consumed); manually driving an EnvWrapper outside a proper vector-env lifecycle; holding references to wrappers that the vector env already retired.","commonSituations":"Custom training loops that manage envs themselves and lose track of which wrappers are exhausted; seed iterators yielding fewer states than the number of resets requested; async/stale references after a NestedExecutor restarts environments.","solutions":["Let the vector environment (e.g. qlib's BaseVectorEnv / tianshou runner) own wrapper recycling; never reset wrappers yourself after exhaustion.","Ensure the seed iterator yields at least as many initial states as resets requested in the phase (train loop steps; FiniteVectorEnv sizes val/test to the iterator).","In custom loops, track exhausted wrappers (reset returns a NaN observation when the iterator merely ends) and only treat the RuntimeError as a lifecycle bug to fix upstream."],"exampleFix":"// before\nfor _ in range(n):\n    obs = wrapper.reset()  # n > number of seeds -> dead wrapper\n// after\nwhile True:\n    obs = wrapper.reset()\n    if is_invalid(obs):  # queue exhausted: stop instead of resetting again\n        break","handlingStrategy":"try-catch","validationCode":"def safe_reset(wrapper):\n    \"\"\"Returns None when the wrapper is dead and should be recycled.\"\"\"\n    try:\n        return wrapper.reset()\n    except RuntimeError as e:\n        if \"dead environment wrapper\" in str(e):\n            return None\n        raise","typeGuard":"def wrapper_is_alive(wrapper) -> bool:\n    return wrapper.seed_iterator is not None","tryCatchPattern":"try:\n    obs = wrapper.reset()\nexcept RuntimeError as e:\n    if \"dead environment wrapper\" in str(e):\n        log.debug(\"wrapper exhausted; recycling\")\n    else:\n        raise","preventionTips":["Let the vector env own wrapper recycling; don't hold or reset wrappers manually.","Size seed iterators to the number of resets your loop performs.","Treat exhausted wrappers (NaN obs) as terminal, not as an error to retry."],"tags":["rl","env-wrapper","lifecycle","seed-iterator","vector-env"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}