• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

omry / omegaconf / 07bca34b-cfd3-4349-a256-b0f3299ac536
100%
master: 100%

Build:
Build:
LAST BUILD BRANCH: main
DEFAULT BRANCH: master
Ran 01 May 2026 08:46PM UTC
Jobs 1
Files 19
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

01 May 2026 08:44PM UTC coverage: 100.0%. Remained the same
07bca34b-cfd3-4349-a256-b0f3299ac536

push

circleci

omry
Fix unsafe_merge crash with Union types (Issue #1087)

## Description
Fixes #1087
Supercedes #1088

This PR fixes an `AssertionError` crash that occurs when `OmegaConf.unsafe_merge` is used to merge configurations containing union types.

### Root Cause Analysis
During `unsafe_merge`, OmegaConf skips deepcopying of nodes (`no_deepcopy_set_nodes = True`). However, to prevent circular references, it still performs a safety check during `_set_item_impl` to see if the node being inserted belongs to the *same root config*:
```python
if self._get_root() is value._get_root():
    do_deepcopy = True
```

When a structured config is expanded and it contains a Union type, OmegaConf synthesizes a detached `UnionNode(MISSING)` and attempts to set it. Because `UnionNode` inherits from `Box`, `isinstance(value, Box)` is `True`, and the code executes `value._get_root()`.

Inside `Node._get_root()`, if a node has no parent, it asserts that it must be a `Container`:
```python
def _get_root(self) -> "Container":
    root: Optional[Box] = self._get_parent()
    if root is None:
        assert isinstance(self, Container)  # <--- CRASH
```
Because `UnionNode` is a `Box` but *not* a `Container`, the assertion fails and the program crashes. This bug is completely specific to `unsafe_merge` combined with `UnionNode`s, as other nodes are either not `Box`es (like `IntegerNode`) or are actually `Container`s (like `DictConfig`).

### The Fix
We safely bypass calling `value._get_root()` on detached nodes during the same-config check in `_set_item_impl`. By acknowledging that a detached node (where `_get_parent() is None`) is conceptually its own root, we use `value` directly in the identity check, completely avoiding the problematic assertion in `base.py`.

## Verification
- Added a parameterized test `test_merge_with_nested_structured_config_and_union_type` that ensures both `merge` and `unsafe_merge` evaluate correctly without crashing.
- Full test suite passes with 0 regressions.

2 of 2 new or added lines in 1 file covered. (100.0%)

3662 of 3662 relevant lines covered (100.0%)

1.0 hits per line

Jobs
ID Job ID Ran Files Coverage
1 07bca34b-cfd3-4349-a256-b0f3299ac536.1 01 May 2026 08:46PM UTC 19
100.0
CircleCI Job
Source Files on build 07bca34b-cfd3-4349-a256-b0f3299ac536
  • Tree
  • List 19
  • Changed 1
  • Source Changed 0
  • Coverage Changed 1
Coverage ∆ File Lines Relevant Covered Missed Hits/Line
  • Back to Repo
  • CircleCI Build #07BCA34B...
  • 192f6863 on github
  • Prev Build on main (#85A87A38...)
  • Next Build on main (#50088B3A...)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc