Skip to content

Fail composition when a dangling override is rescued by an append - #3319

Merged
omry merged 2 commits into
facebookresearch:mainfrom
TheSaiEaranti:dangling-override-validation
Jul 26, 2026
Merged

Fail composition when a dangling override is rescued by an append#3319
omry merged 2 commits into
facebookresearch:mainfrom
TheSaiEaranti:dangling-override-validation

Conversation

@TheSaiEaranti

@TheSaiEaranti TheSaiEaranti commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #3318

Rebased onto main now that #3315 has landed — single commit.

Problem

Per #3318: a Defaults List override with no earlier Group Default correctly fails on its own, but a matching command line append made the composition succeed and silently discarded the appended value:

# config.yaml
defaults:
  - override db: sqlite
$ python app.py +db=mysql      # composed db=sqlite on main and on #3315

Fix

#3315 preserved this behavior deliberately (registering the list's own override when an externally appended group default of the same key resolves), on the principle that a precedence fix shouldn't change unrelated semantics. With #3318 ruling the behavior itself invalid, that registration path is removed. The appended entry now composes without consulting the dangling override, the override is reported unused, and composition fails exactly as it does without the append:

In 'config': Could not override 'db'.
Did you mean to override db?

Per review, config-sourced dangling overrides are now reported directly instead of through the command-line-oriented candidate message:

In '<config>': Invalid Defaults List override '<key>: <value>'.
No earlier Group Default for '<full key>' exists to override.

The diagnostic is byte-identical with and without a command-line append and never suggests candidates. Command-line overrides keep their existing messages. Validity is evaluated against the effective depth-first Defaults List after command-line selections apply — an earlier selection that introduces the target makes a later config override valid, while later appends never do — covered by the five-case dangling_nested_override matrix from review.

Tests

  • group_override_only + +group1=file1ConfigCompositionException. This test fails on Make later Defaults List overrides take precedence #3315 (where it composed), pinning the new semantics.
  • group_override_only with no append → same failure as before the change, guarding against the fix drifting the base case.

pytest tests/defaults_list tests/test_compose.py → 423 passed. Full suite excluding test_completion → 2056 passed.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 25, 2026
A Defaults List override with no earlier Group Default to override correctly
fails on its own, but a matching command line append made the composition
succeed and silently replaced the appended value with the dangling override's.

Stop registering a defaults list's own override when an externally appended
group default of the same key is resolved. The appended entry now composes
without consulting the dangling override, which is then reported unused, so
the composition fails exactly as it does without the append. Command line
overrides of appended groups are registered before traversal and still apply.

Add regression coverage for the dangling override with and without the append.

Fixes facebookresearch#3318
@TheSaiEaranti
TheSaiEaranti force-pushed the dangling-override-validation branch from c93d809 to f48ed4f Compare July 25, 2026 23:23

@omry omry left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for calling out the error message in the description. I agree that it should be improved in this PR.

The behavior is now correct, but with +db=mysql the current diagnostic is confusing:

Could not override 'db'.
Did you mean to override db?

The actual problem is the dangling override db: sqlite in config, not the command-line append. Please report config-sourced dangling overrides directly, for example:

In 'config': Invalid Defaults List override 'db: sqlite'.
No earlier Group Default for 'db' exists to override.

The diagnostic should be identical with and without +db=mysql. Please add regression coverage for both cases. Candidate suggestions for command-line overrides can retain their existing behavior.

omry commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

One clarification to my review: a config-sourced override cannot always fail immediately when first encountered. A command-line selection can change an earlier subtree and thereby introduce the Group Default that makes the later override valid. Please add regression coverage for this full matrix:

  1. An earlier command-line selection introduces db; composition succeeds and the later config override db: sqlite applies.
  2. The selected earlier subtree does not introduce db; composition fails with the dangling config-override error.
  3. db=mysql by itself does not recover the dangling override, because it can only change an existing Group Default.
  4. +db=mysql does not recover it, because the append comes after the config override. This should report the dangling config override without suggesting db as a candidate.
  5. When an earlier selection introduces db, db=mysql succeeds and retains command-line precedence over the config's override db: sqlite.

The validity check therefore needs to use the effective depth-first Defaults List while distinguishing eligible earlier defaults from later command-line appends.

An unused Defaults List override coming from a config is now reported as

    In '<config>': Invalid Defaults List override '<key>: <value>'.
    No earlier Group Default for '<full key>' exists to override.

instead of the command line oriented "Could not override" message with
candidate suggestions, which could suggest the very group the user just
appended. The diagnostic is identical with and without a command line append.
Command line overrides retain their existing messages and suggestions.

Validity is evaluated against the effective depth-first Defaults List, after
command line selections are applied: a config override is legal exactly when
an eligible earlier Group Default exists in the composed tree, so an earlier
selection that introduces the target makes the override valid, while later
command line appends never do. Add regression coverage for the full matrix.
@TheSaiEaranti

Copy link
Copy Markdown
Contributor Author

Both points addressed in 3ca3035.

Diagnostic. Config-sourced unused overrides now report directly, in your suggested form:

In 'dangling_nested_override': Invalid Defaults List override 'group1/group2: file2'.
No earlier Group Default for 'group1/group2' exists to override.

The message is byte-identical with and without a command-line append — both regression tests assert the same string — and never lists candidates, so the appended group is no longer suggested. Command-line overrides keep their existing "Could not override / Did you mean" messages and the +key=value append hint. A side effect worth noting: the nested pre-existing cases (group1/override_invalid*, experiment/error_override_without_*) previously produced the self-contradictory "Could not override 'x'. Did you mean to override x?" — they now get the direct form as well, and their assertions are updated.

The matrix. All five cases are covered by the dangling_nested_override params (fixture: [group1: file1, override group1/group2: file2], where group1=group_item1 selects a subtree that introduces group2):

  1. group1=group_item1 — the selection introduces group1/group2; composition succeeds and the config override applies (group2: file2).
  2. no overrides — no earlier Group Default; fails with the dangling-override error.
  3. group1/group2=file3 — fails; the command-line override cannot introduce the group (existing CLI message retained).
  4. +group1/group2=file3 — fails with the dangling-override error, no candidate suggestion.
  5. group1=group_item1 group1/group2=file3 — composition succeeds with group2: file3; command line takes precedence over the config's override.

On your clarification: the check indeed cannot run at first encounter, and the implementation doesn't — validity falls out of the traversal itself. An override is marked used when it applies to a Group Default during composition of the effective tree (after command-line selections resolve), and the reverse traversal visits appends before any config override registers, so appends can never mark one used. ensure_overrides_used then only has to classify the leftovers, which is where the new message lives. No separate validity pass was needed.

tests/defaults_list + tests/test_compose.py: 428 passed. Full suite excluding test_completion: 2066 passed.

@omry omry left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

All good. Thanks for your help with this and for addressing the review feedback, Sai.

If you would like to chat about Hydra, OmegaConf, Arbiter, OmegaFlow, or Reploy, you are welcome to join the Hydra chat and ping me there.

@omry
omry merged commit a468b0d into facebookresearch:main Jul 26, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dangling Defaults List override can override a command-line append

2 participants