Skip to content

[3.0.0-beta2] Fix rl_games play checkpoint fallback#6133

Merged
ooctipus merged 1 commit into
isaac-sim:release/3.0.0-beta2from
ooctipus:fix/beta2-rlgames-checkpoint-fallback
Jun 11, 2026
Merged

[3.0.0-beta2] Fix rl_games play checkpoint fallback#6133
ooctipus merged 1 commit into
isaac-sim:release/3.0.0-beta2from
ooctipus:fix/beta2-rlgames-checkpoint-fallback

Conversation

@ooctipus

Copy link
Copy Markdown
Collaborator

Cherry-picks #6091 to release/3.0.0-beta2.

Upstream merge commit: de78c80
Cherry-pick commit: e598169

Conflict resolution:

  • Kept the new checkpoint-path test under the beta2 test layout at source/isaaclab_tasks/test/test_checkpoint_path.py.

Validation:

  • git diff --check upstream/release/3.0.0-beta2..HEAD
  • env PYTHONPATH=source/isaaclab_tasks:source/isaaclab:source/isaaclab_rl /home/zhengyuz/Projects/IsaacLab.wt/baseline-newton-fabric-repro/env_isaaclab/bin/python -m py_compile source/isaaclab_tasks/isaaclab_tasks/utils/parse_cfg.py scripts/reinforcement_learning/rl_games/play.py scripts/reinforcement_learning/rl_games/play_rl_games.py scripts/reinforcement_learning/sb3/play.py scripts/reinforcement_learning/sb3/play_sb3.py source/isaaclab_tasks/test/test_checkpoint_path.py
  • env PYTHONPATH=source/isaaclab_tasks:source/isaaclab:source/isaaclab_rl /home/zhengyuz/Projects/IsaacLab.wt/newton-fabric-body-binding/env_isaaclab/bin/python -m pytest -q source/isaaclab_tasks/test/test_checkpoint_path.py
  • env PYTHONPATH=source/isaaclab_tasks_experimental:source/isaaclab_tasks:source/isaaclab_rl:source/isaaclab:source/isaaclab_assets:source/isaaclab_mimic:source/isaaclab_newton:source/isaaclab_physx:source/isaaclab_ov:source/isaaclab_ovphysx:source/isaaclab_visualizers /home/zhengyuz/Projects/IsaacLab.wt/newton-fabric-body-binding/env_isaaclab/bin/python scripts/reinforcement_learning/rl_games/play.py --help
  • env PYTHONPATH=source/isaaclab_tasks_experimental:source/isaaclab_tasks:source/isaaclab_rl:source/isaaclab:source/isaaclab_assets:source/isaaclab_mimic:source/isaaclab_newton:source/isaaclab_physx:source/isaaclab_ov:source/isaaclab_ovphysx:source/isaaclab_visualizers /home/zhengyuz/Projects/IsaacLab.wt/newton-fabric-body-binding/env_isaaclab/bin/python scripts/reinforcement_learning/play.py --rl_library rl_games --help
  • pre-commit run --files docs/source/tutorials/03_envs/run_rl_training.rst scripts/reinforcement_learning/rl_games/play.py scripts/reinforcement_learning/rl_games/play_rl_games.py scripts/reinforcement_learning/sb3/play.py scripts/reinforcement_learning/sb3/play_sb3.py source/isaaclab_tasks/changelog.d/fix-rlgames-checkpoint-fallback.rst source/isaaclab_tasks/isaaclab_tasks/utils/parse_cfg.py source/isaaclab_tasks/test/test_checkpoint_path.py

- add an opt-in fallback checkpoint pattern to the shared
get_checkpoint_path() helper
- use that fallback from rl_games play so short runs can load the latest
available checkpoint when the preferred best-checkpoint file has not
been written yet
- naturally sort numbered checkpoint filenames so epoch 10 is selected
after epoch 9

- python3 -m py_compile
source/isaaclab_tasks/isaaclab_tasks/utils/parse_cfg.py
scripts/reinforcement_learning/rl_games/play.py
scripts/reinforcement_learning/rl_games/play_rl_games.py
source/isaaclab_tasks/test/core/test_checkpoint_path.py
- env
PYTHONPATH=source/isaaclab_tasks:source/isaaclab:source/isaaclab_rl
/home/zhengyuz/Projects/IsaacLab.wt/newton-fabric-body-binding/env_isaaclab/bin/python
-m pytest -q source/isaaclab_tasks/test/core/test_checkpoint_path.py
- pre-commit run --files scripts/reinforcement_learning/rl_games/play.py
scripts/reinforcement_learning/rl_games/play_rl_games.py
source/isaaclab_tasks/isaaclab_tasks/utils/parse_cfg.py
source/isaaclab_tasks/test/core/test_checkpoint_path.py
source/isaaclab_tasks/changelog.d/fix-rlgames-checkpoint-fallback.rst
- env
PYTHONPATH=source/isaaclab_tasks:source/isaaclab:source/isaaclab_rl
/home/zhengyuz/Projects/IsaacLab.wt/feature-unify/env_isaaclab/bin/python
scripts/reinforcement_learning/rl_games/play.py --help
- env
PYTHONPATH=source/isaaclab_tasks:source/isaaclab:source/isaaclab_rl
/home/zhengyuz/Projects/IsaacLab.wt/feature-unify/env_isaaclab/bin/python
scripts/reinforcement_learning/play.py --rl_library rl_games --help

---------

Co-authored-by: jichuanh <jichuanh@nvidia.com>
(cherry picked from commit de78c80)
@github-actions github-actions Bot added bug Something isn't working documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team labels Jun 11, 2026
@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This cherry-pick from #6091 fixes a crash in rl_games and sb3 play scripts that occurred on short training runs where the preferred best/final checkpoint had not yet been written. It introduces a preferred_checkpoint parameter to get_checkpoint_path() so callers can name a specific file to try first, with an automatic fallback to the latest available checkpoint when it is absent. A secondary improvement replaces the old zero-padded alphabetical sort with a true natural sort, so epoch_10 now correctly ranks after epoch_9.

  • parse_cfg.get_checkpoint_path gains preferred_checkpoint: tries that pattern first, falls back to checkpoint if nothing matches, and raises a combined error message when both fail.
  • rl_games play scripts keep --use_last_checkpoint but now pass checkpoint=".*" always and only set preferred_checkpoint to the best-model name when the flag is absent.
  • sb3 play scripts drop --use_last_checkpoint entirely, hardwiring preferred_checkpoint=r"model\.zip" with checkpoint=r"model_.*\.zip" as the fallback.

Confidence Score: 4/5

Safe to merge; the core logic change is small and well-tested, with the only practical concern being the silent removal of --use_last_checkpoint from sb3 scripts

The checkpoint fallback logic is correct and covered by six focused tests. The natural sort key change is a clear improvement with no edge-case hazard. The two items worth watching are: re.match leaving patterns unanchored at the end (consistent with the existing checkpoint behaviour, and harmless with real SB3/rl_games filenames), and the removal of --use_last_checkpoint from the sb3 scripts without a transitional deprecation warning, which will hard-fail any existing automation that passes the flag.

The sb3 play scripts (play_sb3.py and play.py) warrant a second look given the CLI-breaking removal of --use_last_checkpoint; all other files are straightforward.

Important Files Changed

Filename Overview
source/isaaclab_tasks/isaaclab_tasks/utils/parse_cfg.py Adds preferred_checkpoint parameter with try-best-then-fallback logic; also upgrades sort from zero-pad to natural sort — both changes look correct
source/isaaclab_tasks/test/test_checkpoint_path.py New test covering: direct match, wildcard latest, strict-miss ValueError, preferred wins, preferred fallback, and sb3 final-vs-numbered — all six scenarios are correct
scripts/reinforcement_learning/rl_games/play_rl_games.py Switches to preferred_checkpoint fallback pattern; retains --use_last_checkpoint to let callers force latest checkpoint
scripts/reinforcement_learning/sb3/play_sb3.py Removes --use_last_checkpoint flag entirely; hardcodes preferred model.zip with fallback to model_*.zip — breaking CLI change for users who previously passed --use_last_checkpoint
scripts/reinforcement_learning/rl_games/play.py Deprecated wrapper; mirrors play_rl_games.py logic change, retaining --use_last_checkpoint
scripts/reinforcement_learning/sb3/play.py Deprecated wrapper; mirrors play_sb3.py change, removing --use_last_checkpoint without deprecation warning
docs/source/tutorials/03_envs/run_rl_training.rst Drops --use_last_checkpoint from tutorial example command, consistent with sb3 argument removal
source/isaaclab_tasks/changelog.d/fix-rlgames-checkpoint-fallback.rst Changelog entry correctly describes the added preferred_checkpoint parameter and both rl_games/sb3 fixes

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[get_checkpoint_path called] --> B{preferred_checkpoint\nprovided?}
    B -- No --> E[Match files with checkpoint pattern]
    B -- Yes --> C[Match files with preferred_checkpoint]
    C --> D{Any matches?}
    D -- Yes --> G[Natural sort matches]
    D -- No --> E
    E --> F{Any matches?}
    F -- No --> ERR[Raise ValueError]
    F -- Yes --> G
    G --> H[Return last sorted match]
Loading

Reviews (1): Last reviewed commit: "Fix rl_games play checkpoint fallback (#..." | Re-trigger Greptile

Comment on lines +237 to +239
model_checkpoints = [f for f in os.listdir(run_path) if re.match(preferred_checkpoint, f)]
if len(model_checkpoints) == 0:
model_checkpoints = [f for f in os.listdir(run_path) if re.match(checkpoint, f)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 re.match does not anchor at the end of the filename

Both preferred_checkpoint and the existing checkpoint parameter use re.match, which anchors only at the start of the string. A pattern like r"model\.zip" would match model.zip_backup because re.match succeeds as soon as the prefix matches without consuming the rest of the string. Using re.fullmatch (or appending $ to all patterns) would make the behaviour unambiguous and consistent with the intent of selecting an exact named file. This is the same pre-existing behaviour as checkpoint, so widening to re.fullmatch here would need a matching change to line 239 for consistency.

Comment on lines 99 to +105
print("[INFO] Unfortunately a pre-trained checkpoint is currently unavailable for this task.")
return
elif args_cli.checkpoint is None:
if args_cli.use_last_checkpoint:
checkpoint = "model_.*.zip"
else:
checkpoint = "model.zip"
checkpoint_path = get_checkpoint_path(log_root_path, ".*", checkpoint, sort_alpha=False)
# prefer the final model (``model.zip``); fall back to the latest periodic checkpoint when it has
# not been written yet (e.g. short or interrupted runs)
checkpoint_path = get_checkpoint_path(
log_root_path, ".*", r"model_.*\.zip", sort_alpha=False, preferred_checkpoint=r"model\.zip"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 --use_last_checkpoint removed without a deprecation warning

--use_last_checkpoint is removed from both play_sb3.py and play.py (sb3). Any script, CI job, or user invocation that passes --use_last_checkpoint will now fail immediately with argparse's unrecognized arguments error. The rl_games counterparts retain the flag, so the breaking change is only on the sb3 side. Since the new automatic fallback already covers the "short run / no final model" case, the flag is no longer needed — but a short deprecation window (e.g. accepting the flag with a warnings.warn) would smooth the transition for existing automation pipelines.

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review: Fix rl_games play checkpoint fallback

Clean approach to a real usability pain point. The preferred_checkpoint parameter with automatic fallback is a solid design choice—it eliminates the need for users to know about --use_last_checkpoint in the common case while preserving explicit control for power users.

Summary of findings:

  • Natural sort implementation works correctly but has a subtle edge case worth noting
  • The preferred_checkpoint regex is not validated, which could produce confusing errors
  • SB3 raw-string regex patterns are correct and consistent
  • Test coverage is good for the happy path and the fallback path; one gap noted below
  • Documentation update is correct (removes the now-unnecessary flag from the tutorial)

Overall: well-structured change with good test coverage. A few minor suggestions below.

patterns = f"'{checkpoint}'"
if preferred_checkpoint is not None:
patterns = f"'{preferred_checkpoint}' nor '{checkpoint}'"
raise ValueError(f"No checkpoints in the directory: '{run_path}' match {patterns}.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (minor): The natural sort via re.split(r"(\d+)", m) produces empty strings at the boundaries (e.g. re.split(r'(\d+)', '10_foo')['', '10', '_foo']). This works because empty strings sort before non-empty ones, but it means the sort isn't purely "natural" for filenames that start with digits—in that case the leading empty token becomes the primary sort key and all such files cluster together, which is actually fine for checkpoint naming conventions here.

No action needed—just flagging for awareness since the old zero-padded approach (f"{m:0>15}") would break on filenames longer than 15 chars, so this is strictly an improvement.


# list all model checkpoints in the directory
model_checkpoints = [f for f in os.listdir(run_path) if re.match(checkpoint, f)]
# prefer ``preferred_checkpoint`` when given and it matches; otherwise fall back to the general

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (robustness): If preferred_checkpoint is an invalid regex, re.match will raise re.error with a potentially confusing traceback that doesn't mention preferred_checkpoint by name. Consider wrapping this in a try/except that raises a clearer ValueError, e.g.:

try:
    model_checkpoints = [f for f in os.listdir(run_path) if re.match(preferred_checkpoint, f)]
except re.error as e:
    raise ValueError(f"Invalid regex for preferred_checkpoint: '{preferred_checkpoint}': {e}") from e

Same applies to the checkpoint pattern on the next branch. Low priority since callers currently pass known-good patterns, but it's a public API.

checkpoint_path = get_checkpoint_path(log_root_path, ".*", checkpoint, sort_alpha=False)
# prefer the final model (``model.zip``); fall back to the latest periodic checkpoint when it has
# not been written yet (e.g. short or interrupted runs)
checkpoint_path = get_checkpoint_path(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: The raw-string r"model_.*\.zip" and r"model\.zip" patterns are correct. Just confirming that the \. is intentional to avoid matching e.g. model_zip (without the dot)—good defensive regex.

str(tmp_path), ".*", r"model_.*\.zip", sort_alpha=False, preferred_checkpoint=r"model\.zip"
)

assert checkpoint_path == str(expected_checkpoint)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

gap (test coverage): There's no test for the case where preferred_checkpoint matches multiple files. For example, if the regex is r"model\.zip" and somehow both model.zip and (pathologically) another file matches, the sort order determines which wins. It might be worth adding a test that verifies the "last after natural sort" behavior when preferred_checkpoint matches >1 file, ensuring the contract is clear.

Also, no test exercises sort_alpha=False combined with preferred_checkpoint. The sb3 scripts use this combination—would be good to have coverage.

else:
checkpoint_file = f"{agent_cfg['params']['config']['name']}.pth"
resume_path = get_checkpoint_path(log_root_path, run_dir, checkpoint_file, other_dirs=["nn"])
# prefer the best-reward checkpoint (``<name>.pth``); fall back to the latest checkpoint when it has

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

question: With this change, when --use_last_checkpoint is passed, best_checkpoint is None, so preferred_checkpoint=None is passed and the function skips the preferred branch entirely, falling through to the ".*" pattern which matches all checkpoints and picks the latest by natural sort. This is correct and equivalent to the old behavior.

However, should --use_last_checkpoint still be documented/advertised for rl_games, given that the default behavior now auto-falls-back anyway? The flag's only effect is to skip preferring the best checkpoint—which only matters when the best checkpoint does exist but you explicitly don't want it. That's a valid but niche use case; might be worth a comment or docstring update.

@ooctipus
ooctipus merged commit a855496 into isaac-sim:release/3.0.0-beta2 Jun 11, 2026
37 of 38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant