Skip to content

fix(cli): exit cleanly when gym eval submit gets a missing or invalid --config - #3445

Merged
yaoyu-33 merged 3 commits into
NVIDIA-NeMo:mainfrom
sven-rankov:fix-2688-eval-submit-config-errors
Sep 17, 2026
Merged

yaoyu-33 merged 3 commits into
NVIDIA-NeMo:mainfrom
sven-rankov:fix-2688-eval-submit-config-errors

Conversation

@sven-rankov

@sven-rankov sven-rankov commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes #2688 ([VDR][v0.6.0] CLI-8c0bb15d).

gym eval submit --config X was the only --config consumer that let user mistakes escape as Python tracebacks:

  • a path that does not exist (or is a directory) died inside Hydra compose with a ~60-line MissingConfigException traceback whose search-path dump never names the offending path;
  • a file that exists but has the wrong shape leaked a raw pydantic_core.ValidationError traceback, because _handle_pydantic_validation_error only recognises BaseNeMoGymCLIConfig subclasses and SubmitConfig is an orchestration model.

Note: the issue quotes OmegaConf.load at main.py:443 raising FileNotFoundError. Since #3142 the loader is Hydra compose, so the exception type and line moved (MissingConfigException, ~main.py:600), but the symptom is identical and reproduces on current main.

Changes

  • _eval_submit is wrapped in exit_cleanly_on_config_error (the decorator every other gym env/gym eval command already uses) and checks Path(args.config).is_file() before Hydra sees the path, raising a ConfigError that names it.
  • SubmitConfig validation errors are rendered as a ConfigError naming the file, with the same missing required configuration: ... / invalid configuration: path (reason) wording as the generic handler. The field formatting is extracted from _handle_pydantic_validation_error into a shared _describe_validation_errors (pure move, no behaviour change) so the two paths cannot drift.

I went with rendering the error inside _eval_submit rather than the issue's alternative of making SubmitConfig a BaseNeMoGymCLIConfig: that base class carries a sys.argv-sniffing --help validator that does not belong on an orchestration model, and the generic handler's +key=<value> hint would be misleading when the fields live in a YAML file.

Before / after

Same inputs, --dry-run, on main (787ac2b) vs this branch. Exit code is 1 in every error case both before and after; only the output changes.

1. --config points at a file that does not exist (the issue's first case)

$ gym eval submit -c missing.yaml --dry-run

Before β€” 80 lines:

Traceback (most recent call last):
  File ".../bin/gym", line 10, in <module>
    sys.exit(main())
  ...
hydra.errors.MissingConfigException: Cannot find primary config 'missing'. Check that it's in your config search path.

Config search path:
	provider=hydra, path=pkg://hydra.conf
	provider=main, path=file:///abs/path
	provider=schema, path=structured://

After β€” 1 line:

Error: Submit config '/abs/path/missing.yaml' was not found. Check the path is spelled correctly and is relative to your working directory.

(A directory passed as --config gets the same treatment: ... is a directory, not a file.)

2. --config exists but is not a submit config (the issue's second case)

$ printf 'foo: 1\nbar: hello\n' > wrong_shape.yaml
$ gym eval submit -c wrong_shape.yaml --dry-run

Before β€” 41 lines:

Traceback (most recent call last):
  File ".../bin/gym", line 10, in <module>
    sys.exit(main())
  ...
pydantic_core._pydantic_core.ValidationError: 6 validation errors for SubmitConfig
services
  Field required [type=missing, input_value={'foo': 1, 'bar': 'hello'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.13/v/missing
  ...
bar
  Extra inputs are not permitted [type=extra_forbidden, input_value='hello', input_type=str]
    For further information visit https://errors.pydantic.dev/2.13/v/extra_forbidden

After β€” 1 line:

Error: Submit config '/abs/path/wrong_shape.yaml' is invalid: missing required configuration: services, compute, driver, job. invalid configuration: foo (Extra inputs are not permitted); bar (Extra inputs are not permitted).

Errors deeper in the schema keep their location, e.g. invalid configuration: job.output_path (Input should be a valid string), and SubmitConfig's own cross-field @model_validator failures render the same way (<config> (Value error, driver.policy_model 'nope' does not match any service (svc).)) instead of a traceback that echoes the whole config. A valid config produces byte-identical --dry-run output before and after.

Testing

  • tests/unit_tests/test_cli_eval_submit.py: new TestEvalSubmitConfigFileErrors (missing file, directory, wrong shape, nested dotted path, cross-field @model_validator) plus two main()-level tests asserting exit 1, the Error: line, and no traceback. test_typo_in_top_level_key_is_rejected now asserts the ConfigError (and names the rejected key) instead of the raw ValidationError that was the bug.
  • All 8 new/changed tests fail on main without the source change and pass with it.
  • pytest tests/unit_tests/ -m "not sandbox": 4138 passed.
  • pre-commit run on the changed files: clean.
  • Manual CLI check of both error paths and the happy path (--dry-run against a valid config) as above.

Out of scope / follow-up

While reproducing I found that compose() bypasses the compact error rendering that @hydra.main's run_and_report gives every other command, so eval submit also tracebacks on a .yml extension, malformed YAML, a missing defaults: group file, a non-mapping file, a bad override, and an unresolvable interpolation. Those are one root cause and deserve their own issue rather than widening this PR; I can open it with reproductions if that is useful.

Checklist

  • I have read the contributing guidelines.
  • The change is focused; unrelated "drive-by" edits are tracked as separate issues/PRs.
  • Tests added or updated and pass locally, or N/A for docs-only / non-code changes (so CI unit/server checks pass when applicable).
  • Pre-commit checks pass locally (pre-commit run --all-files) (so CI lint/format/copyright pass).
  • All commits have DCO sign-off (git commit -s) (so the DCO check passes).

@copy-pr-bot

copy-pr-bot Bot commented Sep 16, 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.

@nemo-automation-bot nemo-automation-bot Bot added the community-request Issue reported or requested by someone from the community label Sep 16, 2026
…id --config

`_eval_submit` handed `--config` straight to Hydra `compose` and then to
`SubmitConfig.model_validate` without guarding either step. A path that
does not exist therefore died with a `MissingConfigException` traceback,
and a file with the wrong shape leaked a raw pydantic `ValidationError`
traceback, because `_handle_pydantic_validation_error` only recognises
`BaseNeMoGymCLIConfig` subclasses and `SubmitConfig` is an orchestration
model. Every other `--config` consumer reports both mistakes as a single
`Error:` line and exit 1.

- Wrap `_eval_submit` in `exit_cleanly_on_config_error` and check that
  the path is a file before Hydra sees it, raising a `ConfigError` that
  names the path.
- Render `SubmitConfig` validation errors as a `ConfigError` naming the
  file, with the same "missing required configuration: ..." wording the
  generic handler uses; the field formatting is extracted into a shared
  `_describe_validation_errors` so both paths stay identical.

Fixes NVIDIA-NeMo#2688

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Tsvetan (Sven) Rankov <tsvetan.rankov@gmail.com>
@sven-rankov
sven-rankov force-pushed the fix-2688-eval-submit-config-errors branch from fbce1a6 to f82b4e9 Compare September 16, 2026 20:37
@sven-rankov
sven-rankov marked this pull request as ready for review September 16, 2026 20:50
@yaoyu-33 yaoyu-33 added area:cli Command-line interfaces and user-facing command behavior bug Something isn't working complexity:low Localized change in one scope with a small, straightforward review surface needs-review PR is ready for code review and waiting on a reviewer labels Sep 16, 2026
yaoyu-33
yaoyu-33 previously approved these changes Sep 17, 2026
Keep the single-use validation error translation in _eval_submit while preserving the shared field formatter and CLI error contract.

Validation: 291 CLI/orchestration tests passed; 31 eval-submit tests rerun after formatting; pre-commit run --all-files passed. Model rollouts: N/A for behavior-preserving CLI refactor.
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>

@yaoyu-33 yaoyu-33 left a comment

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.

LGTM. The single-use validation wrapper is now inlined, preserving the shared formatter and CLI error behavior. 291 CLI/orchestration tests and pre-commit run --all-files passed; the 31 submit tests were also rerun after formatting. Model rollouts are N/A for this CLI-only change; full CI remains pending.

@yaoyu-33 yaoyu-33 added ready-to-merge PR is approved, current, and waiting for final CI or merge and removed needs-review PR is ready for code review and waiting on a reviewer labels Sep 17, 2026
@yaoyu-33

Copy link
Copy Markdown
Contributor

/ok to test a407938

@github-actions

Copy link
Copy Markdown
Contributor

πŸ”„ Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/35248985180

Merged via the queue into NVIDIA-NeMo:main with commit 88d9d45 Sep 17, 2026
41 checks passed

This branch was previously deployed

1 inactive deployment
public β€” a4079389 Deployed Sep 17, 2026 by copy-pr-bot[bot] via release / finalize / notify #3113
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:cli Command-line interfaces and user-facing command behavior bug Something isn't working community-request Issue reported or requested by someone from the community complexity:low Localized change in one scope with a small, straightforward review surface ready-to-merge PR is approved, current, and waiting for final CI or merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[VDR][v0.6.0] CLI-8c0bb15d Β· gym eval submit prints a raw Python traceback for a missing or invalid --config

2 participants