capture_screenshot calls Page.captureScreenshot without checking that the target is visible. Chromium suspends canvas compositing for a hidden tab, so the call returns the last frame that tab painted — a real PNG of the right chart showing the wrong data.
This is hard to catch by eye because the DOM keeps updating underneath: the header, the quote and the status-bar clock in the returned image are current, while the candles, the price axis and any Pine tables are frozen at whatever was on screen when the tab was last foregrounded.
Reproduce
- Two chart tabs open, A in front, B behind.
- Attach to B (
tab_switch, or point ~/.tv-mcp/active-target.json at it).
chart_set_symbol on B to something clearly different from what B showed before.
capture_screenshot.
The PNG shows B's previous symbol/date. chart_get_state correctly reports the new one, so the tool result and the image disagree and only the image is wrong.
Observed on 2.3.0 / Desktop 3.3.0 / macOS, with a chart in a background tab of a second Desktop window: three consecutive captures 40s apart were byte-similar frozen charts whose only changing element was the clock.
Why it matters
An agent building a report treats the PNG as evidence. A stale frame of the correct instrument is the worst failure shape available here — it is plausible, it is silent, and it survives review.
Suggested fix
src/core/capture.js already has everything it needs nearby: src/core/tab.js implements _isTargetVisible() via document.visibilityState. Asserting that before the CDP capture, and failing with a clear category (or at minimum returning visible: false in the result so callers can refuse), would close it.
const vis = await evaluate('document.visibilityState');
if (vis !== 'visible') throw new ClassifiedError(CATEGORIES.CHART_LOADING,
'Target tab is hidden; a capture would return a stale frame', { hint: 'Front the tab with tab_switch first.' });
Happy to send a PR if the shape looks right.
capture_screenshotcallsPage.captureScreenshotwithout checking that the target is visible. Chromium suspends canvas compositing for a hidden tab, so the call returns the last frame that tab painted — a real PNG of the right chart showing the wrong data.This is hard to catch by eye because the DOM keeps updating underneath: the header, the quote and the status-bar clock in the returned image are current, while the candles, the price axis and any Pine tables are frozen at whatever was on screen when the tab was last foregrounded.
Reproduce
tab_switch, or point~/.tv-mcp/active-target.jsonat it).chart_set_symbolon B to something clearly different from what B showed before.capture_screenshot.The PNG shows B's previous symbol/date.
chart_get_statecorrectly reports the new one, so the tool result and the image disagree and only the image is wrong.Observed on 2.3.0 / Desktop 3.3.0 / macOS, with a chart in a background tab of a second Desktop window: three consecutive captures 40s apart were byte-similar frozen charts whose only changing element was the clock.
Why it matters
An agent building a report treats the PNG as evidence. A stale frame of the correct instrument is the worst failure shape available here — it is plausible, it is silent, and it survives review.
Suggested fix
src/core/capture.jsalready has everything it needs nearby:src/core/tab.jsimplements_isTargetVisible()viadocument.visibilityState. Asserting that before the CDP capture, and failing with a clear category (or at minimum returningvisible: falsein the result so callers can refuse), would close it.Happy to send a PR if the shape looks right.