Skip to content

fix(config): surface missing dependencies when resolving _target_ - #4631

Merged
akoumpa merged 1 commit into
NVIDIA-NeMo:mainfrom
0z5a:config-resolve-target-missing-dep
Nov 4, 2026
Merged

akoumpa merged 1 commit into
NVIDIA-NeMo:mainfrom
0z5a:config-resolve-target-missing-dep

Conversation

@0z5a

@0z5a 0z5a commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

What does this PR do ?

When a _target_ module exists but one of its own imports is missing, the error names the wrong thing. For example, without einops (it is not a base dependency), resolving the target of examples/multimodal_finetune/bagel/bagel_sft.yaml, nemo_automodel.components.models.bagel.configuration.BagelBackendConfig, fails with:

ModuleNotFoundError: Module 'nemo_automodel.components.models' has no submodule 'bagel'. Available model submodules in this installation: ['bagel', ...

The message also suggests the installation may be too old, even though bagel is in the list it prints. With this PR the same call raises ModuleNotFoundError: No module named 'einops'.

_resolve_target() tries shorter module prefixes whenever importlib.import_module() raises ModuleNotFoundError, including when the missing module is a dependency of the candidate rather than the candidate itself.

Changelog

  • components/config/loader.py, _resolve_target(): fall back to a shorter prefix only when the missing module (e.name) is the candidate or one of its parents; otherwise re-raise the original error. When e.name is unset, the fallback is unchanged.
  • tests/unit_tests/config/test_loader.py: test_resolve_target_reports_missing_dependency_of_existing_module resolves a temporary module that imports a missing package and expects that package's name in the error.

Testing

main at 613cb7c, CPU, Python 3.12.

main this PR
tests/unit_tests/config new test: 1 failed 107 passed
bagel _target_ with einops blocked "has no submodule 'bagel'" "No module named 'einops'"

ruff check and ruff format --check pass on the changed files.

Code provenance

Not applicable.

Before your PR is "Ready for review"

Pre checks:

  • Make sure you read and followed Contributor guidelines
  • Did you write any new necessary tests? Yes, one in test_loader.py.
  • Did you add or update any necessary documentation? Not needed.

Additional Information

_resolve_target() tries shorter module prefixes whenever import_module()
raises ModuleNotFoundError. When the module exists but one of its own
imports is missing, it also fell back, and the user saw an unrelated
error instead of the missing package. For example, without einops,
resolving the BagelBackendConfig target of the bagel SFT example reports
that nemo_automodel.components.models has no submodule 'bagel' and that
the install may be too old, while listing bagel among available models.

Fall back to a shorter prefix only when the missing module is the
candidate itself or one of its parents; otherwise re-raise the original
ModuleNotFoundError, which names the missing package.

Co-authored-by: Claude Opus 5.5 <[email protected]>
Signed-off-by: 0z5a <[email protected]>
@0z5a
0z5a requested a review from a team as a code owner November 3, 2026 02:58
@copy-pr-bot

copy-pr-bot Bot commented Nov 3, 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.

@HuiyingLi HuiyingLi 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.

Suggestion: Approve

Reviewed b6cb009a39676347f56dec63efab7048fccbf7e5, including both changed files and the config/CLI/model-import consumers. No blocking findings. Checking the structured missing-module name preserves the existing longest-prefix resolution while keeping internal dependency failures intact. This belongs in the shared resolver used by _target_ and *_fn.

Validation on Python 3.12.3:

  • All 107 native configuration tests and 17 independent CPU import-boundary tests passed.
  • Real temporary modules/packages covered nested class/method and namespace resolution, missing candidate/parent fallback, transitive and sibling dependencies, similarly prefixed package names, unnamed exceptions, generic ImportError, and native CLI target/callback handling.
  • The new regression test fails on the actual merge base with only its test overlaid; three normal resolution controls pass.
  • With einops deliberately unavailable in an isolated process, the actual BagelBackendConfig import reports No module named 'einops' and retains the bagel/autoencoder.py traceback. The identical check fails on the old implementation, which instead reports the existing bagel package as missing.

These are CPU import/configuration checks; no model construction, downloads, training or GPU execution was needed.

@HuiyingLi

Copy link
Copy Markdown
Contributor

/ok to test b6cb009

@akoumpa
akoumpa merged commit 6f4dff1 into NVIDIA-NeMo:main Nov 4, 2026
79 checks passed

This branch was previously deployed

3 inactive deployments
public — b6cb009a Deployed Nov 3, 2026 by copy-pr-bot[bot] via release / finalize / notify #6448
test — b6cb009a Deployed Nov 3, 2026 by copy-pr-bot[bot] via cicd-wait-in-queue #12608
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants