koala73 / koala73/worldmonitor
test(e2e): name the cause of `Object with guid response@… was not bound in the connection` in variant-smoke
- Dominant language
- TypeScript
- Stars
- 86.6k
- Forks
- 13.1k
- Avg merge
- 8h 4m
- Merged PRs (30d)
- 825
Description
Split out of #7837, which closed its measurement half and left this open. Related: #6501 (open, same underlying event, friendlier message), #5685 (closed, retry mitigation only).
**This issue is written to be picked up cold by an agent.** Everything below was re-verified against the installed `playwright-core@1.58.2` and the live repo on 2026-09-08. Line numbers are from `node_modules`, so re-check them after any Playwright bump.
## The signature
```
Object with guid response@<32-hex> was not bound in the connection
```
Appears on `variant-smoke (1)` / `variant-smoke (2)`, reddens whichever test is current, and has never reproduced locally.
## What is definitively known
### Throw site
`node_modules/playwright-core/lib/client/connection.js:187-193`, in `_tChannelImplFromWire`:
```js
const object = this._objects.get(arg.guid);
if (!object)
throw new Error(`Object with guid ${arg.guid} was not bound in the connection`);
```
It runs while **decoding an incoming message**, from `Connection.dispatch` (`:134-177`), on one of exactly two paths:
| Path | Line | Consequence |
| --- | --- | --- |
| **Command result** | `:151` `callback.resolve(validator2(result, …))` | The callback was already removed at `:144` (`this._callbacks.delete(id)`), so **the originating API call never settles** — it hangs rather than rejecting. |
| **Event** | `:176` `object._channel.emit(method, validator(params, …))` | The validator throws *before* `emit`, so no listener body ever runs. |
### Why no spec-side code can prevent it
`dispatch` is invoked inside `setImmediate` (`node_modules/playwright-core/lib/inProcessFactory.js:46`):
```js
dispatcherConnection.onmessage = (message) => setImmediate(() => clientConnection.dispatch(message));
```
So the throw escapes as a process-level `uncaughtException`, caught by `node_modules/playwright/lib/worker/workerMain.js:67` → `unhandledError` (`:144-161`). It reaches **no awaited promise and no listener body**. `try/catch` in a spec, defensive accessors, and `.catch()` handlers are all structurally incapable of stopping it — `variant-live-smoke`'s capture helper already try/catches every accessor it touches and the throw still escaped.
### The failing test name is not evidence
`unhandledError` fails `this._currentTest` — whatever test the worker happens to be running when the throw surfaces. With `workers: 4` and `fullyParallel: true`, that test can be unrelated to the context that owned the disposed object. **Do not start the investigation from the test that went red.**
### Both Sep-7 traces show the command-result shape, not an event
Recorded in #7837 and worth re-checking against the preserved artifacts below:
- Run `34079140563` attempt 1: contexts 0 and 1 completed their full loop including `Close context` (9.596 s, 17.249 s); the throw landed during context 2's `Navigate to "/dashboard"` at 17.335 s **with no `after` record**.
- Run `34079495998` attempt 1: landed on the **first** `Navigate`, 0.092 s, again with no `after`.
A `Navigate` step that never gets an `after` is the pending-forever shape from `:151` — a `Frame.goto` result carrying `response@…` arriving after a `__dispose__` of the page/context subtree had already overtaken it. Neither leftover context produced a screenshot or trace chunk even though the runner screenshots `_allPages()` and stops tracing for `_allContexts()` on failure, which means the page was already gone browser-side.
That makes this **the same underlying event as #6501**, observed at a different instant: #6501 catches the browser vanishing ~6.5 s into the first dashboard `goto` and renders it as `Target page, context or browser has been closed`. Here the target loss races a serialized result instead, and Playwright renders the opaque guid message.
### Upstream is not a fix
`microsoft/playwright` #21210, #22210, #24370, #27704, #28214, #28320, #28504, #20023 — all closed, all 2023. The only code fix, PR #28629 (gc buckets, v1.41), is already in 1.58.2 and does not apply at our dispatcher volumes. The throw is unchanged in upstream `main`; latest published is **1.63.0** (checked 2026-09-08). **Upgrading Playwright is not a remedy — do not propose it as one.**
## Preserved evidence — expires 2026-09-21, act before then
Both original failing artifacts are still live (verified 2026-09-08). Download them **first**, before doing anything else, because they cannot be regenerated:
```bash
gh api repos/koala73/worldmonitor/actions/artifacts/10003158303/zip > smoke1-34079140563.zip # 131 MB, expires 2026-09-21T03:21:07Z
gh api repos/koala73/worldmonitor/actions/artifacts/10003276789/zip > smoke1-34079495998.zip # 106 MB, expires 2026-09-21T03:27:38Z
```
Both heads share the identical git tree `3f08a12d2e52380f404117268b819757d6690798`, so the tree is not the variable.
Each zip contains per-test `trace.zip` files. Open with `npx playwright show-trace ` and read the **step timeline**, specifically: which contexts reached `Close context`, which step lacks an `after` record, and the wall-clock offset of the last step before the throw.
## What is NOT known
- Whether the browser process died (crash/OOM) or the protocol simply reordered. **This is the open question.** Everything above is consistent with both.
- Whether it correlates with a specific spec, worker index, or position in the shard.
- Why it has never reproduced locally, including at `--workers=4 --retries=0` across the full shard.
## Investigation order
1. Preserve the two artifacts above (they expire).
2. Land the diagnostic capture from the companion issue — it is the only step that produces the discriminating evidence, and nobody has run it on #5685, #6501, or #7837.
3. Wait for a natural occurrence. Do not chase a local repro; ~50 local runs across two sessions have produced zero.
4. When it fires, read `channel.log` per the decision table in the companion issue.
## Rules for whoever picks this up
- **Do not** add retries, raise `retries` above 1, skip a test, or wrap anything in a swallow to make it green. `retries: 1` already exists in `playwright.config.ts` as an explicit #5685 mitigation with a documented rationale; it is a mitigation, not a fix, and this issue exists because it did not prevent recurrence.
- **Do not** "fix" this in spec code. Re-read *Why no spec-side code can prevent it* — a PR that adds a `try/catch` to a spec is a null change and will be rejected.
- **Do not** upgrade Playwright as a remedy (see above).
- **Do** treat a passing rerun as nothing. A rerun that goes green is the normal behaviour of this defect, not evidence about it.
## Acceptance criteria
- [x] Both preserved artifacts downloaded and stored somewhere that outlives 2026-09-21, with the location recorded in this issue.
- [ ] A `pw:channel` capture from a real occurrence is attached, or an explicit statement that N runs over a stated window produced no occurrence.
- [ ] The last `__dispose__` / `
Contributor guide
Research direction
First download the two preserved GitHub Actions artifacts with the issue's gh commands before they expire. Inspect their trace.zip files with npx playwright show-trace, then review playwright.config.ts and land the companion issue's diagnostic capture. On a real occurrence, use channel.log to identify the last disposal and determine whether browser death or protocol ordering caused the missing Navigate result.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- playwright, typescript
- Domain
- testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100