Skip to content

fix(envs): handle async vector env compatibility for Libero#3321

Closed
reeceomahoney wants to merge 5 commits intohuggingface:mainfrom
reeceomahoney:fix/libero-async-envs
Closed

fix(envs): handle async vector env compatibility for Libero#3321
reeceomahoney wants to merge 5 commits intohuggingface:mainfrom
reeceomahoney:fix/libero-async-envs

Conversation

@reeceomahoney
Copy link
Copy Markdown
Contributor

@reeceomahoney reeceomahoney commented Apr 8, 2026

Type / Scope

  • Type: Bug
  • Scope: envs

Summary / Motivation

When using AsyncVectorEnv with Libero environments, the default fork context causes Mujoco to throw the following error mujoco.FatalError: Offscreen framebuffer is not complete, error 0x8cdd

What changed

  • src/lerobot/envs/factory.py: Use context="spawn" when creating AsyncVectorEnv via functools.partial.
  • src/lerobot/envs/utils.py:
    • Added _has_env_attr() helper that checks sub-environment attributes using env.envs for sync envs and env.call() for async envs.
    • Updated are_all_envs_same_type() to return True for async envs (sub-env types can't be inspected across processes).
    • Updated check_env_attributes_and_types() and add_envs_task() to use the new helper.

Related issues

None

How was this tested

  • Manual testing with Libero environments using use_async_envs=true

Checklist (required before merge)

  • Linting/formatting run (pre-commit run -a)
  • All tests pass locally (pytest)
  • Documentation updated
  • CI is green

Reviewer notes

  • The are_all_envs_same_type function assumes True for async envs since we can't inspect types across process boundaries. This is a reasonable default since the env factory creates all sub-envs from the same constructor.

reeceomahoney and others added 5 commits March 19, 2026 22:28
Without persistent_workers, the dataloader cycle() tears down and
respawns workers each epoch, leaking threads until the OS limit is hit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 8, 2026 14:15
@github-actions github-actions bot added the evaluation For issues or PRs related to environment evaluation, and benchmarks. label Apr 8, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR fixes compatibility issues when using AsyncVectorEnv with Libero by avoiding fork-related crashes and removing direct reliance on env.envs (which isn’t available for async vector envs).

Changes:

  • Create async vector envs with context="spawn" to avoid fork-safety issues (MuJoCo/OpenGL).
  • Add a helper to probe sub-env capabilities in both sync and async vector envs.
  • Adjust env-utility checks and task extraction to work when sub-envs live in subprocesses.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
src/lerobot/envs/utils.py Adds async-safe attribute probing and updates task/type utility logic accordingly
src/lerobot/envs/factory.py Switches async env construction to use multiprocessing spawn context

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +133 to +141
def _has_env_attr(env: gym.vector.VectorEnv, attr: str) -> bool:
"""Check if sub-environments have an attribute, compatible with sync and async vector envs."""
if hasattr(env, "envs"):
return hasattr(env.envs[0], attr)
try:
env.call(attr)
return True
except Exception:
return False
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_has_env_attr() swallows all exceptions from env.call(attr). This can mask real failures inside an existing method (e.g., task_description() raising), incorrectly treating it as 'missing'. Prefer catching only the 'missing attribute/method' error (typically AttributeError propagated from workers, or the specific vector-env error type) and re-raising other exceptions so actual bugs aren’t hidden.

Copilot uses AI. Check for mistakes.
Comment on lines +172 to 173
if _has_env_attr(env, "task_description"):
task_result = env.call("task_description")
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the async case, _has_env_attr() calls env.call(...) and then add_envs_task() calls env.call(...) again, doubling cross-process IPC and potentially invoking the method twice (side effects / non-determinism). Consider restructuring to attempt env.call('task_description') once (handling only the 'method missing' case), otherwise fall back to env.call('task').

Copilot uses AI. Check for mistakes.
Comment on lines +184 to 185
elif _has_env_attr(env, "task"):
task_result = env.call("task")
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the async case, _has_env_attr() calls env.call(...) and then add_envs_task() calls env.call(...) again, doubling cross-process IPC and potentially invoking the method twice (side effects / non-determinism). Consider restructuring to attempt env.call('task_description') once (handling only the 'method missing' case), otherwise fall back to env.call('task').

Copilot uses AI. Check for mistakes.
Comment on lines +156 to 159
if not (_has_env_attr(env, "task_description") or _has_env_attr(env, "task")):
warnings.warn(
"The environment does not have 'task_description' and 'task'. Some policies require these features.",
UserWarning,
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the warning condition from 'warn unless both are present' (previous not (A and B)) to 'warn only if neither is present' (not (A or B)). If the intent is to preserve prior behavior, this should remain an and check (or the warning message should be updated to match the new semantics).

Copilot uses AI. Check for mistakes.
Comment on lines +167 to +169
env_cls = (
partial(gym.vector.AsyncVectorEnv, context="spawn") if use_async_envs else gym.vector.SyncVectorEnv
)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using partial(...) means env_cls is no longer a class when use_async_envs=True. If any downstream code expects class-like behavior (e.g., isinstance(..., env_cls), env_cls.__name__, type comparisons), this will break. A more robust approach is to keep env_cls as the class and pass context='spawn' at the construction site (or branch on async vs sync for instantiation), so the variable’s type stays consistent.

Copilot uses AI. Check for mistakes.
Comment on lines +133 to +134
def _has_env_attr(env: gym.vector.VectorEnv, attr: str) -> bool:
"""Check if sub-environments have an attribute, compatible with sync and async vector envs."""
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The helper name/docstring says 'attr', but the async path effectively validates a no-arg callable via env.call(attr). Renaming to something like _has_env_method() (or clarifying in the docstring that this is specifically for env.call-invoked methods) would make expectations clearer and reduce misuse.

Copilot uses AI. Check for mistakes.
@reeceomahoney
Copy link
Copy Markdown
Contributor Author

Closed by #3274

@reeceomahoney reeceomahoney deleted the fix/libero-async-envs branch April 9, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

evaluation For issues or PRs related to environment evaluation, and benchmarks.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants