Skip to content

Commit 768c305

Browse files
Report why fetching a pre-trained checkpoint failed (#7535)
# Description Running a play script with `--checkpoint pretrained` on a machine whose `.pretrained_checkpoints` directory was left behind by a container run (owned by `root`) prints: ``` Fetching pre-trained checkpoint : https://.../rsl_rl/Isaac-Humanoid-Direct_newtonmjwarp_none_rsl_rl.pt A pre-trained checkpoint is currently unavailable for this task. ``` and exits. The checkpoint is published and reachable; the download failed with `PermissionError: [Errno 13] Permission denied: '.../.pretrained_checkpoints/rsl_rl/https'`. That error was discarded by a bare `except Exception` in `isaaclab_rl.utils.pretrained_checkpoint.get_published_pretrained_checkpoint`, which reported every failure — a missing checkpoint, an unreachable server, an unwritable cache, a full disk — with the same sentence and returned `None`, after which the play scripts return without further explanation. This separates the two cases: * **The asset server does not provide the checkpoint.** `None` is still returned and the message still starts with `A pre-trained checkpoint is currently unavailable for this task.`, so callers and tests matching on that line are unaffected. It now also names the location that was tried, the task and backends that location was derived from, and what to do instead: ``` A pre-trained checkpoint is currently unavailable for this task. The asset server does not provide 'https://.../Isaac-Not-A-Real-Task_newtonmjwarp_none_rsl_rl.pt'. Either no checkpoint is published for task 'Isaac-Not-A-Real-Task' with the 'newtonmjwarp' physics and 'none' render backends, or the asset server could not be reached. Train the task, or pass --checkpoint <path> to use a checkpoint of your own. ``` * **The checkpoint exists but could not be downloaded.** This is a local problem the user has to fix, so it raises `RuntimeError` naming the checkpoint, the cache directory and the originating error, which is chained as the cause: ``` RuntimeError: Failed to download the pre-trained checkpoint 'https://.../Isaac-Humanoid-Direct_newtonmjwarp_none_rsl_rl.pt' into '/home/user/IsaacLab/.pretrained_checkpoints/rsl_rl': PermissionError: [Errno 13] Permission denied: '/home/user/IsaacLab/.pretrained_checkpoints/rsl_rl/https'. Check that the cache directory is writable and that the disk is not full; a directory left behind by a container run is owned by root. ``` Note that `omni.client` reports a checkpoint that was never published and a server it cannot reach identically, so both remain covered by the "unavailable" message. Both paths were verified end to end against the live asset server, using a published checkpoint with a read-only cache directory and an unpublished checkpoint name. ## Type of change - Bug fix (non-breaking change which fixes an issue) Behaviour note: callers that relied on `None` to mask a local download failure now see a `RuntimeError`. Callers that treat `None` as "no checkpoint published for this task" — every caller in the repository — are unchanged. This is called out in the changelog fragment. ## Release backport - [ ] <!-- backport-active-release --> Backport this pull request to the active release branch after it merges into `develop` ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
1 parent 59df9b8 commit 768c305

3 files changed

Lines changed: 107 additions & 3 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Fixed
2+
^^^^^
3+
4+
* Fixed :func:`~isaaclab_rl.utils.pretrained_checkpoint.get_published_pretrained_checkpoint` reporting
5+
every download failure as ``A pre-trained checkpoint is currently unavailable for this task.``. A
6+
checkpoint the asset server does not provide is still reported that way, but the message now names the
7+
location that was tried, the task and backends it was derived from, and what to do instead.
8+
9+
Changed
10+
^^^^^^^
11+
12+
* Changed :func:`~isaaclab_rl.utils.pretrained_checkpoint.get_published_pretrained_checkpoint` to raise
13+
``RuntimeError`` when a published checkpoint cannot be downloaded, for instance when the
14+
``.pretrained_checkpoints`` cache directory is not writable, instead of returning ``None``. The
15+
originating error is chained as the cause. ``None`` is now returned only when the asset server does not
16+
report the checkpoint, which covers both an unpublished checkpoint and a server that could not be
17+
reached; callers that relied on ``None`` to mask local download failures must catch ``RuntimeError``.

source/isaaclab_rl/isaaclab_rl/utils/pretrained_checkpoint.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,16 @@ def get_published_pretrained_checkpoint(
239239
to use the legacy checkpoint layout.
240240
241241
Returns:
242-
The path.
242+
The path, or None when the asset server does not report a checkpoint for this task
243+
and backend combination. That covers both a checkpoint that was never published and
244+
a server that could not be reached, which ``omni.client`` does not distinguish, so a
245+
transient outage is not evidence that a checkpoint does not exist. The reason is
246+
printed before returning.
247+
248+
Raises:
249+
RuntimeError: If the checkpoint is published but could not be downloaded, for
250+
instance because the local cache directory is not writable. The originating
251+
error is chained as the cause.
243252
"""
244253
filename = get_pretrained_checkpoint_filename(workflow, task_name, physics_backend, render_backend)
245254
ov_path = get_published_pretrained_checkpoint_path(workflow, task_name, physics_backend, render_backend)
@@ -252,9 +261,35 @@ def get_published_pretrained_checkpoint(
252261
print(f"Fetching pre-trained checkpoint : {ov_path}")
253262
try:
254263
resume_path = retrieve_file_path(ov_path, download_dir)
255-
except Exception:
256-
print("A pre-trained checkpoint is currently unavailable for this task.")
264+
except FileNotFoundError:
265+
# the asset server reports a checkpoint that was never published and a server it
266+
# cannot reach the same way, so both are covered by the same message
267+
backends = (
268+
""
269+
if physics_backend is None
270+
else f" with the '{physics_backend}' physics and '{render_backend}' render backends"
271+
)
272+
print(
273+
"A pre-trained checkpoint is currently unavailable for this task.\n"
274+
f" The asset server does not provide '{ov_path}'.\n"
275+
f" Either no checkpoint is published for task '{task_name}'{backends}, or the asset"
276+
" server could not be reached.\n"
277+
" Train the task, or pass --checkpoint <path> to use a checkpoint of your own."
278+
)
257279
return None
280+
except Exception as exc:
281+
# the checkpoint exists on the server, so this is a local failure the user has to fix;
282+
# reporting it as an unavailable checkpoint would send them looking in the wrong place
283+
hint = ""
284+
if isinstance(exc, OSError):
285+
hint = (
286+
" Check that the cache directory is writable and that the disk is not full;"
287+
" a directory left behind by a container run is owned by root."
288+
)
289+
raise RuntimeError(
290+
f"Failed to download the pre-trained checkpoint '{ov_path}' into"
291+
f" '{os.path.abspath(download_dir)}': {type(exc).__name__}: {exc}.{hint}"
292+
) from exc
258293
else:
259294
print("Using pre-fetched pre-trained checkpoint")
260295
return resume_path

source/isaaclab_rl/test/test_pretrained_checkpoint.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,55 @@ def _retrieve_file_path(remote_path: str, download_dir: str) -> str:
174174
"remote_path": "omniverse://IsaacLab/PretrainedCheckpoints/rsl_rl/Isaac-Cartpole_physx_none_rsl_rl.pt",
175175
"download_dir": expected_download_dir,
176176
}
177+
178+
179+
def test_get_published_pretrained_checkpoint_reports_unpublished_checkpoint(
180+
monkeypatch: pytest.MonkeyPatch,
181+
tmp_path: Path,
182+
capsys: pytest.CaptureFixture[str],
183+
):
184+
"""Test that a checkpoint missing from the asset server names the location that was tried."""
185+
monkeypatch.chdir(tmp_path)
186+
monkeypatch.setattr(pretrained_checkpoint, "ISAACLAB_NUCLEUS_DIR", "omniverse://IsaacLab")
187+
188+
def _retrieve_file_path(remote_path: str, download_dir: str) -> str:
189+
raise FileNotFoundError(f"Unable to find the file: {remote_path}")
190+
191+
monkeypatch.setattr(pretrained_checkpoint, "retrieve_file_path", _retrieve_file_path)
192+
193+
path = pretrained_checkpoint.get_published_pretrained_checkpoint(
194+
"rsl_rl",
195+
"Isaac-Cartpole",
196+
"physx",
197+
"none",
198+
)
199+
200+
assert path is None
201+
output = capsys.readouterr().out
202+
assert "A pre-trained checkpoint is currently unavailable for this task." in output
203+
assert "omniverse://IsaacLab/PretrainedCheckpoints/rsl_rl/Isaac-Cartpole_physx_none_rsl_rl.pt" in output
204+
assert "'physx' physics and 'none' render backends" in output
205+
206+
207+
def test_get_published_pretrained_checkpoint_raises_on_unwritable_cache(
208+
monkeypatch: pytest.MonkeyPatch,
209+
tmp_path: Path,
210+
):
211+
"""Test that a local download failure is reported instead of being reported as unavailable."""
212+
monkeypatch.chdir(tmp_path)
213+
monkeypatch.setattr(pretrained_checkpoint, "ISAACLAB_NUCLEUS_DIR", "omniverse://IsaacLab")
214+
cause = PermissionError(13, "Permission denied", str(tmp_path / ".pretrained_checkpoints"))
215+
216+
def _retrieve_file_path(remote_path: str, download_dir: str) -> str:
217+
raise cause
218+
219+
monkeypatch.setattr(pretrained_checkpoint, "retrieve_file_path", _retrieve_file_path)
220+
221+
with pytest.raises(RuntimeError) as error:
222+
pretrained_checkpoint.get_published_pretrained_checkpoint("rsl_rl", "Isaac-Cartpole", "physx", "none")
223+
224+
message = str(error.value)
225+
assert "Isaac-Cartpole_physx_none_rsl_rl.pt" in message
226+
assert str(tmp_path / ".pretrained_checkpoints" / "rsl_rl") in message
227+
assert "Permission denied" in message
228+
assert error.value.__cause__ is cause

0 commit comments

Comments
 (0)