Skip to content

fix: create the rollout output directory before the first write - #2313

Merged
ananthsub merged 1 commit into
NVIDIA-NeMo:mainfrom
ananthsub:fix/rollout-output-parent-dir
Aug 5, 2026
Merged

fix: create the rollout output directory before the first write#2313
ananthsub merged 1 commit into
NVIDIA-NeMo:mainfrom
ananthsub:fix/rollout-output-parent-dir

Conversation

@ananthsub

@ananthsub ananthsub commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #2217

Problem

gym eval run --no-serve --output results/mcqa_rollouts.jsonl — the quickstart command in the README — raises FileNotFoundError when results/ does not exist yet:

File "nemo_gym/rollout_collection.py", line 508, in run_from_config
    with config.materialized_jsonl_fpath.open("wb") as f:
FileNotFoundError: [Errno 2] No such file or directory: 'results/mcqa_rollouts_materialized_inputs.jsonl'

The mkdir was already present in run_from_config, but it is called too late. It ran at the top of the dispatch section, while the first thing the run writes is the materialized inputs.

Fix

Move output_fpath.parent.mkdir(parents=True, exist_ok=True) to the top of run_from_config, above both the resume and fresh branches.

The issue suggested adding a mkdir at each of the four output paths. One is enough: all four derive from output_fpath and keep its parent: materialized inputs and aggregate metrics via with_stem, the failures sidecar via with_name, rollouts is the path itself.

Resume semantics are unchanged: creating an empty directory does not make output_fpath.exists() or materialized_jsonl_fpath.exists() true, so resume eligibility is decided the same way as before.

Why this only showed up on the PyPI install path

Two things were hiding it:

  • Without --no-serve, gym eval run dispatches to e2e_rollout_collection, which sets output_dirpath = <output parent>/preprocessed_datasets and lets TrainDataProcessor create it, incidentally creating the output parent. Only the --no-serve path goes straight to collect_rollouts.
  • The repo tracks a results/.gitignore placeholder, so results/ exists in a git clone. A PyPI install running from an arbitrary cwd has no such directory.

Scope

This was the only affected entry point. The others already create their output parent first, so this brings rollout collection in line with the existing convention:

  • gym eval reverifyrollout_reverification.py, in _prepare_output_fpaths
  • gym eval aggregaterollout_collection.py, in RolloutAggregationHelper.run_from_config
  • gym dataset renderprompt.py, in materialize_prompts
  • gym eval profile — writes next to an input rollouts file that must already exist

Testing

Added test_run_from_config_creates_missing_output_dir, parametrized over resume_from_cache, writing to a two-level-deep missing directory and asserting all four artifacts land there. Both parametrizations fail on main and pass with this change.

@copy-pr-bot

copy-pr-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@ananthsub
ananthsub force-pushed the fix/rollout-output-parent-dir branch from 4e80b93 to e023e48 Compare August 4, 2026 15:00
@ananthsub ananthsub added the r0.5.0 Auto-cherrypick to release branch. Apply before merge; cherrypick happens after merge. label Aug 4, 2026
ffrujeri
ffrujeri previously approved these changes Aug 5, 2026
@github-actions github-actions Bot added the sla:review-overdue Review response is over the one-business-day SLA label Aug 5, 2026
…IA-NeMo#2217)

`gym eval run --no-serve --output results/rollouts.jsonl` raised
FileNotFoundError when `results/` did not exist yet. The mkdir was already
there, but 11 lines too late: it ran at the top of the dispatch section,
while the first thing the run writes is the materialized inputs.

Move the mkdir to the top of `run_from_config`. All four artifacts a run
produces derive from `output_fpath` and keep its parent -- materialized
inputs and aggregate metrics via `with_stem`, the failures sidecar via
`with_name` -- so one mkdir covers all of them, and it now precedes both
the resume and fresh branches.

Only `--no-serve` was affected. Without it, `gym eval run` dispatches to
`e2e_rollout_collection`, which creates `<output parent>/preprocessed_datasets`
during data preparation and so created the parent as a side effect. A git
clone also hid this, because the repo tracks a `results/.gitignore`
placeholder; a PyPI install running from an arbitrary cwd has no such
directory.

The other output-writing entry points already do this: `gym eval reverify`
(rollout_reverification.py), `gym eval aggregate` (rollout_collection.py),
and `gym dataset render` (prompt.py). This brings rollout collection in line.

Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
@ananthsub
ananthsub merged commit 59aa5e6 into NVIDIA-NeMo:main Aug 5, 2026
28 checks passed
@ananthsub
ananthsub deleted the fix/rollout-output-parent-dir branch August 5, 2026 17:11
OlegSudakov pushed a commit to OlegSudakov/Gym that referenced this pull request Aug 7, 2026
…IA-NeMo#2313)

Fixes NVIDIA-NeMo#2217

## Problem

`gym eval run --no-serve --output results/mcqa_rollouts.jsonl` — the
quickstart command in the README — raises `FileNotFoundError` when
`results/` does not exist yet:

```
File "nemo_gym/rollout_collection.py", line 508, in run_from_config
    with config.materialized_jsonl_fpath.open("wb") as f:
FileNotFoundError: [Errno 2] No such file or directory: 'results/mcqa_rollouts_materialized_inputs.jsonl'
```

The mkdir was already present in `run_from_config`, but it is called too
late. It ran at the top of the dispatch section, while the first thing
the run writes is the materialized inputs.

## Fix

Move `output_fpath.parent.mkdir(parents=True, exist_ok=True)` to the top
of `run_from_config`, above both the resume and fresh branches.

The issue suggested adding a mkdir at each of the four output paths. One
is enough: all four derive from `output_fpath` and keep its parent:
materialized inputs and aggregate metrics via `with_stem`, the failures
sidecar via `with_name`, rollouts is the path itself.

Resume semantics are unchanged: creating an empty directory does not
make `output_fpath.exists()` or `materialized_jsonl_fpath.exists()`
true, so resume eligibility is decided the same way as before.

## Why this only showed up on the PyPI install path

Two things were hiding it:

- Without `--no-serve`, `gym eval run` dispatches to
`e2e_rollout_collection`, which sets `output_dirpath = <output
parent>/preprocessed_datasets` and lets `TrainDataProcessor` create it,
incidentally creating the output parent. Only the `--no-serve` path goes
straight to `collect_rollouts`.
- The repo tracks a `results/.gitignore` placeholder, so `results/`
exists in a git clone. A PyPI install running from an arbitrary cwd has
no such directory.

## Scope

This was the only affected entry point. The others already create their
output parent first, so this brings rollout collection in line with the
existing convention:

- `gym eval reverify` — `rollout_reverification.py`, in
`_prepare_output_fpaths`
- `gym eval aggregate` — `rollout_collection.py`, in
`RolloutAggregationHelper.run_from_config`
- `gym dataset render` — `prompt.py`, in `materialize_prompts`
- `gym eval profile` — writes next to an input rollouts file that must
already exist

## Testing

Added `test_run_from_config_creates_missing_output_dir`, parametrized
over `resume_from_cache`, writing to a two-level-deep missing directory
and asserting all four artifacts land there. Both parametrizations fail
on `main` and pass with this change.

Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

r0.5.0 Auto-cherrypick to release branch. Apply before merge; cherrypick happens after merge. sla:review-overdue Review response is over the one-business-day SLA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[VDR] QS-22b494eb · gym eval run --output does not create the parent directory, breaking the PyPI install path

2 participants