#1770·visdom

[Bug Report] ImagePane crashes with TypeError for image_history when selected is undefined or out of bounds

Author: Pcmhacker-heroCreated Aug 27, 2026Updated Aug 30, 2026

Bug Description

While working on the frontend visualization components, I noticed a reproducible crash in ImagePane.js when rendering image_history panes. If an image_history window payload is received without a selected field (or if the selection index becomes invalid after history truncation or updates), the frontend throws an unhandled TypeError inside React's render loop and crashes the entire dashboard grid.

Looking into how other history-based panes are implemented (such as PlotPane.js and exportTemplate.js), PlotPane explicitly guards against missing selection indices and out-of-bounds array access. ImagePane.js currently misses these defensive checks.


Code Analysis & Root Cause

  1. Unchecked State Initialization: In js/panes/ImagePane.js:41:

    javascript
    const [actualSelected, setActualSelected] = useState(props.selected);

    When props.selected is not explicitly provided in the window properties, actualSelected is initialized to undefined. In comparison, PlotPane.js handles this via:

    javascript
    const [actualSelected, setActualSelected] = useState(
      isHistory ? selected || 0 : 0
    );
  2. Unconditional Prop Sync: In js/panes/ImagePane.js:243-245:

    javascript
    useEffect(() => {
      setActualSelected(selected);
    }, [selected]);

    If a parent re-render occurs where selected is undefined, this unconditionally overwrites any valid internal state back to undefined.

  3. Array Indexing Without Bounds Clamping: In js/panes/ImagePane.js:396:

    javascript
    content = content[actualSelected];

    If actualSelected is undefined (or if it points to an index beyond content.length after images were pruned or updated), content becomes undefined.

  4. Unhandled Property Access during Render: Immediately following that line (lines 405 and 408):

    javascript
    useEffect(() => {
      let cancelled = false;
      typesetMathJax(captionRef.current, () => cancelled);
      return () => {
        cancelled = true;
      };
    }, [content.caption]);
    
    if (content.caption) { ... }

    Evaluating content.caption throws TypeError: Cannot read properties of undefined (reading 'caption'). Because Visdom does not wrap individual grid panes inside separate React error boundaries, this uncaught exception causes the whole layout grid to unmount.


Reproduction Steps

  1. Launch a Visdom server: python -m visdom.server
  2. Open the web interface at http://localhost:8097/
  3. In a Python terminal or client script, send an image_history window payload where selected is omitted (or load an environment JSON saved without selected):
    python
    import visdom
    viz = visdom.Visdom()
    # Send an image_history window payload without setting the selected index
    viz._send({
        "data": [{
            "type": "image_history",
            "content": {
                "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
                "caption": "Frame 1"
            }
        }],
        "win": "img_history_test",
        "eid": "main",
        "opts": {"show_slider": True}
    }, endpoint="events")
  4. Check the browser console.

Expected behavior

The ImagePane component should gracefully handle missing selected props by defaulting to index 0 (or content.length - 1), clamp the index within valid array bounds [0, content.length - 1], and safely access properties using optional chaining so that the pane renders cleanly without breaking the grid.


Client logs:

Uncaught TypeError: Cannot read properties of undefined (reading 'caption')
    at ImagePane (ImagePane.js:405)
    at renderWithHooks (react-dom.development.js:14985)
    at mountIndeterminateComponent (react-dom.development.js:17811)
    at beginWork (react-dom.development.js:18596)

Server logs:

200 POST /events (127.0.0.1) 1.45ms

Additional context

This bug affects any scenario where:

  • An environment is reloaded from disk containing image_history data exported without explicit selected keys.
  • A custom API client / mock sends pane data directly to /events.
  • An active image_history window has its frames truncated (e.g., when reaching memory capacity limits) while a user had a higher slider index selected.