microsoft/qlib · warning · NotImplementedError

Render is not implemented in EnvWrapper.

Error message

Render is not implemented in EnvWrapper.

What it means

NotImplementedError from `EnvWrapper.render` (qlib/rl/utils/env_wrapper.py:250). The wrapper proxies reset/step to the underlying simulator plus interpreters, but rendering was never implemented, so any call — including generic RL tooling that renders by default — fails.

Source

Thrown at qlib/rl/utils/env_wrapper.py:250

        self.status["reward_history"].append(rew)

        if self.aux_info_collector is not None:
            aux_info = self.aux_info_collector(sim_state)
        else:
            aux_info = {}

        # Final logging stuff: RL-specific logs
        if done:
            self.logger.add_scalar("steps_per_episode", self.status["cur_step"])
        self.logger.add_scalar("reward", rew)
        self.logger.add_any("obs", obs, loglevel=LogLevel.DEBUG)
        self.logger.add_any("policy_act", policy_action, loglevel=LogLevel.DEBUG)

        info_dict = InfoDict(log=self.logger.logs(), aux_info=aux_info)
        return obs, rew, done, info_dict

    def render(self, mode: str = "human") -> None:
        raise NotImplementedError("Render is not implemented in EnvWrapper.")

View on GitHub (pinned to 79633dd950)

Solutions

  1. Remove render calls; inspect simulator state and `logger` metrics (wrapper logs obs/reward per step) instead.
  2. Use the wrapper's `info_dict['log']` / LoggerWriter outputs to visualize training.
  3. If rendering is essential, subclass EnvWrapper and implement `render` from `self.simulator.get_state()` for your task.

Example fix

// before
obs, rew, done, info = env.step(act)
env.render()  // NotImplementedError
// after
obs, rew, done, info = env.step(act)
print(info["log"])  # use logged metrics instead of rendering
Defensive patterns

Strategy: type-guard

Validate before calling

def supports_render(env) -> bool:
    return type(env).render is not object.__getattribute__(env, "render").__func__  # simplified
# simpler and sufficient:
def supports_render(env) -> bool:
    import inspect
    try:
        return "Not implemented" not in inspect.getsource(type(env).render)
    except (OSError, TypeError):
        return True

Type guard

from qlib.rl.utils.env_wrapper import EnvWrapper

def env_supports_render(env) -> bool:
    return not isinstance(env, EnvWrapper) or type(env).render is not EnvWrapper.render

Try / catch

try:
    env.render(mode=mode)
except NotImplementedError:
    log.debug("render unsupported; skipping visualization")

Prevention

When it happens

Trigger: Calling `env.render()` or `env.render('human')` directly; using a tianshou/gym runner or experiment tracker with `render=True`; gym API conformance checks or wrappers that invoke render on reset/step.

Common situations: Reusing generic gym visualization utilities with qlib's wrapper; watch/monitor wrappers (RecordVideo etc.) applied around EnvWrapper; porting tutorial code from other gym environments that call render in the loop.

Related errors


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/ba5001abdd9ec153. Report an issue: GitHub.