Skip to content

max_bad_states never restarts the simulator (_should_restart is write-only) #390

Description

@aadi-joshi

Summary

TaskManagerConfig.max_bad_states is documented as restarting the simulator after a run of consecutive bad episodes (for example, the agent leaving the expected app). The TaskManager side of that path runs: it counts bad episodes, logs that a restart is happening, and increments restart stats. The actual simulator relaunch never happens, because the flag it sets is never read by the Coordinator (or anything else).

Current behavior

When DumpsysThread reports that the user exited the expected app, TaskManager._determine_transition_fn() calls _increment_bad_state() and returns a truncation:

https://github.com/google-deepmind/android_env/blob/main/android_env/components/task_manager.py

In _increment_bad_state():

  1. _bad_state_counter is incremented.
  2. Once counter >= max_bad_states (default 3), it logs:
    Too many consecutive bad states. Restarting simulator.
  3. stats['restart_count_max_bad_states'] is incremented.
  4. self._should_restart = True is set.

The consecutive-counter logic itself looks intentional and consistent with the config:

  • reset_task() only clears _bad_state_counter when the previous episode was not a bad episode, so consecutive bad episodes accumulate as documented.
  • TaskManagerConfig.max_bad_states is documented as: if that many episodes finish in a bad state in a row, restart the simulation.

The gap is on the Coordinator side. Coordinator.rl_reset() only relaunches when the simulator is unhealthy or a periodic restart is due:

if not self._simulator_healthy or self._should_periodic_relaunch():
  self._launch_simulator()

It never consults the TaskManager bad-state signal. A search of the repo shows _should_restart is written in exactly one place and has no readers. It is also never initialized in TaskManager.__init__, and there are no unit tests for this restart path in task_manager_test.py or coordinator_test.py.

Net effect after N consecutive "user exited" truncations:

  • Logs claim the simulator is restarting.
  • restart_count_max_bad_states increases.
  • The emulator process is left running; only a normal episode reset runs.

Expected behavior

Per TaskManagerConfig and the docstring on _increment_bad_state, after max_bad_states consecutive bad episodes the simulator should be relaunched, in the same recovery class as periodic restart / unhealthy-simulator relaunch. Stats and logs should reflect a restart that actually occurred.

Impact

For long-running training, repeated activity-check / "user exited" failures currently only truncate episodes. The recovery path that max_bad_states is meant to provide never fires, so the environment can stay in a stale or sticky bad UI state. The existing log line and restart_count_max_bad_states counter also make debugging misleading, because they report restarts that did not happen.

Suggested fix

I think this is unfinished wiring rather than a design change. A small fix that mirrors the existing periodic-restart path:

  1. Initialize the flag in TaskManager and expose it (for example a should_restart() getter).
  2. In Coordinator.rl_reset(), also call _launch_simulator() when that flag is set, alongside the existing unhealthy / periodic checks.
  3. Clear the flag after a successful relaunch (and reset the bad-state counter as appropriate).
  4. Add unit tests for the TaskManager signal and for Coordinator picking it up on reset.

Happy to open a PR along those lines if this matches the intended design.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions