Skip to content

A fractional or oversized viewport fails in CDP but stays stored, so later responses misreport it and every lighthouse_audit on the page fails #2966

Description

@coygeek

Description of the bug

emulate accepts a viewport whose width or height is fractional (800.5x600x1) or above Chrome's limit (20000000x600x1). Chrome rejects the resulting Emulation.setDeviceMetricsOverride call, and the tool returns a raw protocol error, but the rejected viewport has already been stored as the page's emulation state. From then on every response on that page prints Emulating viewport: {...,"width":800.5,...} although the page is not emulated, and every lighthouse_audit on the page runs the audit and then fails with the same protocol error, discarding the result. The state stays poisoned until the agent calls emulate again without the bad viewport.

Actual behavior

Both builds:

emulate {viewport:"800.5x600x1"} -> isError true
  Emulating viewport: {"deviceScaleFactor":1,"isMobile":false,"hasTouch":false,"isLandscape":false,"width":800.5,"height":600}
  Error: Protocol error (Emulation.setDeviceMetricsOverride): Invalid parameters Failed to deserialize params.width - BINDINGS: int32 value expected at position 22
evaluate_script -> {"w":1200,"h":2029,"dpr":1}     (page not emulated)
take_screenshot -> isError false, response includes Emulating viewport: {...,"width":800.5,"height":600}
lighthouse_audit {mode:"snapshot"} -> isError true after about 1.3 s, same Emulation.setDeviceMetricsOverride int32 error, no audit result
emulate {viewport:"20000000x600x1"} -> isError true
  Error: Protocol error (Emulation.setDeviceMetricsOverride): Width and height values must be positive, not greater than 10000000
lighthouse_audit {mode:"snapshot"} -> isError true, same "not greater than 10000000" error
emulate {} -> "Emulation configured successfully"
lighthouse_audit {mode:"snapshot"} -> normal result

Reproduction

  1. Serve any HTML page from a loopback HTTP server at http://127.0.0.1:PORT/min.html, for example:
<!doctype html><html lang="en"><head><meta charset="utf-8"><title>min</title></head><body><h1>Hello</h1></body></html>
  1. Start the server under test with the flags above and send these tools/call requests in order:
{"name":"new_page","arguments":{"url":"http://127.0.0.1:PORT/min.html"}}
{"name":"emulate","arguments":{"pageId":2,"viewport":"800.5x600x1"}}
{"name":"evaluate_script","arguments":{"pageId":2,"function":"() => ({w: innerWidth, h: innerHeight, dpr: devicePixelRatio})"}}
{"name":"take_screenshot","arguments":{"pageId":2}}
{"name":"lighthouse_audit","arguments":{"pageId":2,"mode":"snapshot"}}
{"name":"emulate","arguments":{"pageId":2,"viewport":"20000000x600x1"}}
{"name":"lighthouse_audit","arguments":{"pageId":2,"mode":"snapshot"}}
{"name":"emulate","arguments":{"pageId":2}}
{"name":"lighthouse_audit","arguments":{"pageId":2,"mode":"snapshot"}}

The sequence was run once per build with identical results; an earlier independent run on both builds gave the same results, including lighthouse_audit in the default navigation mode.

Expectation

The viewport parameter is documented as '<width>x<height>x<devicePixelRatio>[,mobile][,touch][,landscape]' (docs/tool-reference.md, emulate), and the server already validates it before use: viewportTransform (src/tools/ToolDefinition.ts:500-545 at the main build) rejects non-positive or non-finite values with a readable Invalid viewport width ... message. A value Chrome cannot apply should either be rejected the same way, or, if it reaches Chrome and fails, leave the page's emulation state as it was, so that later responses report the emulation actually in effect and lighthouse_audit can restore it.

MCP configuration

--headless --isolated --no-usage-statistics --no-performance-crux unless the reproduction states otherwise (update checks disabled with CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS=1).

Chrome DevTools MCP version

1.10.1 (npm) and main 5ddb0a3 (local build)

Chrome version

154.0.8037.98 (stable)

Node version

v22.23.2

Operating system

macOS 27.0

Environment details

  • chrome-devtools-mcp 1.10.1 from npm (the release build).
  • chrome-devtools-mcp built from main at commit 5ddb0a3 (the main build).
  • Google Chrome 154.0.8037.98 stable, headless; Node v22.23.2; macOS 27.
  • Both builds were started with --headless --isolated --no-usage-statistics --no-performance-crux and CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS=1, and driven by a scripted stdio MCP client.

Evidence

  • Expected source: emulate.viewport format in docs/tool-reference.md and the existing input validation in viewportTransform (src/tools/ToolDefinition.ts:500-545, main build).
  • Failure source: scripted MCP runs above on both builds. In McpPage.emulate() (src/McpPage.ts:1104-1110 on the main build, src/McpPage.ts:969-975 at the 1.10.1 tag), this.emulationSettings = Object.keys(newSettings).length ? newSettings : {} is assigned before await page.setViewport(newSettings.viewport ?? null) runs, and lighthouse_audit re-applies the stored settings through page.restoreEmulation() in its finally block (src/tools/lighthouse.ts:124-126).
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Fix check

With the steps above, emulate {viewport:"800.5x600x1"} and emulate {viewport:"20000000x600x1"} either return a readable validation error or succeed with a value Chrome accepts; in no case does a later response print Emulating viewport with a width the page does not have, and both lighthouse_audit calls return a normal audit result. As controls, a valid emulate {viewport:"800x600x1"} still emulates an 800-pixel-wide page and is still listed in later responses, and the existing non-positive checks still reject 0x600.

Additional context

The full open and closed issue and PR corpus was screened as of 2026-10-06. Closed #2662, fixed by PR #2663, added the current positivity and finiteness checks in viewportTransform but not integer or upper-bound checks. Closed #2115 ("emulate is not atomic ...") reported the opposite desync: a throw from setViewport before the state was stored. Its fix, PR #2134, moved setViewport after the state commit and timeout update so that a viewport-triggered reload gets the throttled timeout; with that order, a setViewport call that Chrome rejects now leaves the rejected viewport stored. resize_page has a similar raw Browser.setContentsSize int32 error for fractional sizes; it is a different tool and code path.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions