#1795·visdom

Bug: compare_envs crashes or half-merges when a contributing plot pane has no data

Author: thisisanubhavCreated Sep 3, 2026Updated Sep 6, 2026

Bug

compare_envs in py/visdom/utils/server_utils.py guards the base env's plot data before indexing it, but not the contributing env's. The plot branch reads:

python
base_data = destWidJson["content"].get("data") or []
if not base_data or "name" not in base_data[0]:
    continue                                  # base side: guarded
...
else:
    destWidJson["has_compare"] = True
    for _dataIdx, data in enumerate(win["content"]["data"]):   # contributor side: not guarded

This is the same class of problem as #1739, which was fixed for the base side only. Two distinct failures remain on the contributor side.

1. KeyError: 'data' when a contributing pane has no data key

A plot pane whose content exists but carries no "data" key makes win["content"]["data"] raise. The base side already uses .get("data"), which implies this shape is considered reachable.

Reproduced as a unit test against compare_envs:

KeyError: 'data'
  py/visdom/utils/server_utils.py:615

2. A contributor with no traces produces a one-sided "comparison"

has_compare is set to True before iterating the contributor's traces. If that list is empty, the loop body never runs, so nothing is appended and nothing clears the flag. The pane then survives the has_compare filter while containing only the base env's data.

Reproduced with env a holding a loss plot with one trace and env b holding a same-titled plot with no traces:

titles=['loss', 'compare_legend']  win_present=True  traces=['a_train']

The pane is presented as a comparison of two envs while showing one env's trace.

This also contradicts the behaviour the suite already pins for the analogous case. test_a_later_env_with_an_unnamed_trace_clears_has_compare documents the intended rule as "One malformed contributor withdraws the whole pane, rather than half-merging" — an empty contributor should withdraw the pane the same way, but currently does not.

Expected

  • A contributing pane without a "data" key is skipped rather than crashing the comparison.
  • A contributing pane with no traces withdraws the pane, consistent with the unnamed-trace rule.

Fix

Read the contributor's data with .get("data") or [] and clear has_compare when it is empty, mirroring the base-side guard. I have a fix and regression tests ready and will open a PR.