[GH-ISSUE #7763] [Bug] capture_browser_scenario: a response with HTTP status above 599 throws a ZodError in a Playwright event handler and ends the MCP server #4637

Closed
opened 2026-10-31 00:51:41 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @Qin2Qin on GitHub (Oct 28, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7763

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

MCP or agent integration

Observed behavior

capture_browser_scenario with capture.events: ["network"] terminates the whole rea mcp process (exit status 1, no JSON-RPC response to the call) when any response in the scenario has an HTTP status above 599.

The scenario's response event is validated with browserScenarioEventSchema.parse(...) in PlaywrightScenarioEvents.push, and the response event allows only status 100–599. push runs inside REA's listener for Playwright's BrowserContext response event, so the ZodError is thrown from an event-emitter callback and becomes an uncaught exception that ends the process.

Statuses above 599 occur on the public web. LinkedIn, for example, answers 999 to requests it classifies as automated. Our agents hit this crash once in 75 MCP server starts, on a public marketing page; we did not record which resource answered above 599.

Reproduction

Reproduced on every attempt: 2 of 2 on 6.8.0, 1 of 1 on 6.7.0.

  1. Serve a page whose one subresource answers 999:

    from http.server import BaseHTTPRequestHandler, HTTPServer
    class Handler(BaseHTTPRequestHandler):
        def do_GET(self):
            body = b'<!doctype html><title>status 999</title><img src="/pixel.gif">' if self.path == "/" else b""
            self.send_response(200 if self.path == "/" else 999)
            self.send_header("Content-Type", "text/html")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)
    HTTPServer(("127.0.0.1", 8765), Handler).serve_forever()
    

    curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8765/pixel.gif prints 999.

  2. Start rea mcp over stdio, send initialize and notifications/initialized, then:

    {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "capture_browser_scenario", "arguments": {
      "browser": {"mode": "launch", "executable_path": "<Playwright chromium_headless_shell-1208>"},
      "start_url": {"url": "http://127.0.0.1:8765/"},
      "actions": [{"step_id": "settle", "action": "wait_for_timeout", "duration_ms": 1000}],
      "capture": {"events": ["network"]}}}}
    
  3. Within 3 seconds the server prints the ZodError below and exits with status 1. Only the initialize request gets a response.

Expected behavior

The scenario completes and the response is recorded with its status, or it is omitted with a typed limitation. The MCP server stays up either way. Chromium and Playwright accept any three-digit status (Playwright reported 999 here). A validation failure in an event handler should not be able to end the process.

Impact and scope

One response above 599 anywhere in a scenario ends the MCP server, and with it every tool and all retained session state of the connected agent. Untested: the CLI capture-browser-scenario path, and whether other event producers also call .parse() inside Playwright event handlers.

REA version

6.8.0 (npm); also 6.7.0

Runtime and platform

Node 26.8.2, macOS arm64 (local reproduction on 6.7.0 and 6.8.0); Node 26.5.1, Linux arm64 container (6.7.0, the occurrence in agent runs); Playwright 1.58.2 headless shell, revision 1208

Target and artifact context

Public web pages; the local page above is enough to reproduce it

Interface

MCP

Earliest failure stage

Capture or observation

Relevant output or evidence

stderr of rea mcp 6.8.0 for the request above (paths shortened to <node_modules>):

file://<node_modules>/zod/v4/core/parse.js:13
        throw e;
        ^
ZodError: [
  {
    "origin": "number",
    "code": "too_big",
    "maximum": 599,
    "inclusive": true,
    "path": [
      "status"
    ],
    "message": "Too big: expected number to be <=599"
  }
]
    at PlaywrightScenarioEvents.push (file://<node_modules>/rea-agents/dist/mcp/browserScenario-CGgBqm7N.js:2451:47)
    at Object.emit (file://<node_modules>/rea-agents/dist/mcp/browserScenario-CGgBqm7N.js:2343:29)
    at PlaywrightScenarioNetwork.response (file://<node_modules>/rea-agents/dist/mcp/browserScenario-CGgBqm7N.js:2011:36)
    at BrowserContext.onResponse (file://<node_modules>/rea-agents/dist/mcp/browserScenario-CGgBqm7N.js:2364:50)
    at BrowserContext._callHandler (<node_modules>/playwright-core/lib/client/eventEmitter.js:66:29)
    at BrowserContext.emit (<node_modules>/playwright-core/lib/client/eventEmitter.js:56:12)
    at BrowserContext._onResponse (<node_modules>/playwright-core/lib/client/browserContext.js:179:10)

6.7.0 fails with the same error at PlaywrightScenarioEvents.push (dist/mcp/browserScenario-BRUz7yN0.js:2044:47).

Typed outcome or recovery expected

The call should return the scenario with the response recorded, or a typed limitation naming the out-of-range response. If capture cannot continue, it should return a typed error. It should not end the process.

Analysis or suspected cause

Source-derived, from the published 6.8.0 package:

  • dist/domain/browserScenarioCaptureValues.js:178: the response event schema declares status: z.int().min(100).max(599).
  • dist/browser/PlaywrightScenarioEvents.js:181-201: push(event) calls browserScenarioEventSchema.parse(...) synchronously.
  • The stack above shows push called from REA's BrowserContext response listener (PlaywrightScenarioNetwork.response), so the exception escapes into Playwright's event emitter.

Two possible fixes, neither tested: widen the schema to 100–999, or use safeParse and record a limitation.

Additional context

Not a regression that I know of: 6.7.0 and 6.8.0 behave the same.

Originally created by @Qin2Qin on GitHub (Oct 28, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7763 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area MCP or agent integration ### Observed behavior `capture_browser_scenario` with `capture.events: ["network"]` terminates the whole `rea mcp` process (exit status 1, no JSON-RPC response to the call) when any response in the scenario has an HTTP status above 599. The scenario's response event is validated with `browserScenarioEventSchema.parse(...)` in `PlaywrightScenarioEvents.push`, and the response event allows only `status` 100–599. `push` runs inside REA's listener for Playwright's `BrowserContext` `response` event, so the `ZodError` is thrown from an event-emitter callback and becomes an uncaught exception that ends the process. Statuses above 599 occur on the public web. LinkedIn, for example, answers 999 to requests it classifies as automated. Our agents hit this crash once in 75 MCP server starts, on a public marketing page; we did not record which resource answered above 599. ### Reproduction Reproduced on every attempt: 2 of 2 on 6.8.0, 1 of 1 on 6.7.0. 1. Serve a page whose one subresource answers 999: ```python from http.server import BaseHTTPRequestHandler, HTTPServer class Handler(BaseHTTPRequestHandler): def do_GET(self): body = b'<!doctype html><title>status 999</title><img src="/pixel.gif">' if self.path == "/" else b"" self.send_response(200 if self.path == "/" else 999) self.send_header("Content-Type", "text/html") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) HTTPServer(("127.0.0.1", 8765), Handler).serve_forever() ``` `curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8765/pixel.gif` prints `999`. 2. Start `rea mcp` over stdio, send `initialize` and `notifications/initialized`, then: ```json {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "capture_browser_scenario", "arguments": { "browser": {"mode": "launch", "executable_path": "<Playwright chromium_headless_shell-1208>"}, "start_url": {"url": "http://127.0.0.1:8765/"}, "actions": [{"step_id": "settle", "action": "wait_for_timeout", "duration_ms": 1000}], "capture": {"events": ["network"]}}}} ``` 3. Within 3 seconds the server prints the `ZodError` below and exits with status 1. Only the `initialize` request gets a response. ### Expected behavior The scenario completes and the response is recorded with its status, or it is omitted with a typed limitation. The MCP server stays up either way. Chromium and Playwright accept any three-digit status (Playwright reported 999 here). A validation failure in an event handler should not be able to end the process. ### Impact and scope One response above 599 anywhere in a scenario ends the MCP server, and with it every tool and all retained session state of the connected agent. Untested: the CLI `capture-browser-scenario` path, and whether other event producers also call `.parse()` inside Playwright event handlers. ### REA version 6.8.0 (npm); also 6.7.0 ### Runtime and platform Node 26.8.2, macOS arm64 (local reproduction on 6.7.0 and 6.8.0); Node 26.5.1, Linux arm64 container (6.7.0, the occurrence in agent runs); Playwright 1.58.2 headless shell, revision 1208 ### Target and artifact context Public web pages; the local page above is enough to reproduce it ### Interface MCP ### Earliest failure stage Capture or observation ### Relevant output or evidence stderr of `rea mcp` 6.8.0 for the request above (paths shortened to `<node_modules>`): ```text file://<node_modules>/zod/v4/core/parse.js:13 throw e; ^ ZodError: [ { "origin": "number", "code": "too_big", "maximum": 599, "inclusive": true, "path": [ "status" ], "message": "Too big: expected number to be <=599" } ] at PlaywrightScenarioEvents.push (file://<node_modules>/rea-agents/dist/mcp/browserScenario-CGgBqm7N.js:2451:47) at Object.emit (file://<node_modules>/rea-agents/dist/mcp/browserScenario-CGgBqm7N.js:2343:29) at PlaywrightScenarioNetwork.response (file://<node_modules>/rea-agents/dist/mcp/browserScenario-CGgBqm7N.js:2011:36) at BrowserContext.onResponse (file://<node_modules>/rea-agents/dist/mcp/browserScenario-CGgBqm7N.js:2364:50) at BrowserContext._callHandler (<node_modules>/playwright-core/lib/client/eventEmitter.js:66:29) at BrowserContext.emit (<node_modules>/playwright-core/lib/client/eventEmitter.js:56:12) at BrowserContext._onResponse (<node_modules>/playwright-core/lib/client/browserContext.js:179:10) ``` 6.7.0 fails with the same error at `PlaywrightScenarioEvents.push (dist/mcp/browserScenario-BRUz7yN0.js:2044:47)`. ### Typed outcome or recovery expected The call should return the scenario with the response recorded, or a typed limitation naming the out-of-range response. If capture cannot continue, it should return a typed error. It should not end the process. ### Analysis or suspected cause Source-derived, from the published 6.8.0 package: - `dist/domain/browserScenarioCaptureValues.js:178`: the `response` event schema declares `status: z.int().min(100).max(599)`. - `dist/browser/PlaywrightScenarioEvents.js:181-201`: `push(event)` calls `browserScenarioEventSchema.parse(...)` synchronously. - The stack above shows `push` called from REA's `BrowserContext` `response` listener (`PlaywrightScenarioNetwork.response`), so the exception escapes into Playwright's event emitter. Two possible fixes, neither tested: widen the schema to 100–999, or use `safeParse` and record a limitation. ### Additional context Not a regression that I know of: 6.7.0 and 6.8.0 behave the same.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4637
No description provided.