Skip to content

[BUG] Browser promise detection rejects valid thenable proxies #197

Closed

Description

@jerome-benoit

Duplicate issue

Description

Browser promise detection calls Object.getPrototypeOf() before assimilating ordinary thenables. A valid thenable proxy can reject solely because its unrelated prototype trap throws. Normal tasks, abortable tasks and cleanup handlers are affected.

Steps to reproduce

Use the browser bundle built from #196 at 69ec6511deb18761ae6eb6fb4eb841c47e3bfc3c. Serve these files alongside dist/browser/mod.js.

worker.js:

import { ThreadWorker } from './dist/browser/mod.js'

new ThreadWorker(() => new Proxy(
  { then(resolve) { resolve(42) } },
  { getPrototypeOf() { throw new Error('opaque prototype') } },
))

Browser entry module:

import { FixedThreadPool } from './dist/browser/mod.js'

const pool = new FixedThreadPool(1, new URL('./worker.js', import.meta.url))
if (!pool.info.ready) {
  await new Promise(resolve => {
    pool.eventTarget.addEventListener('ready', resolve, { once: true })
  })
}
try {
  console.log(await pool.execute())
} finally {
  await pool.destroy()
}

Expected behavior

Resolve 42 by invoking the callable then. Thenable assimilation does not require prototype inspection.

Actual behavior

Execution rejects with opaque prototype before invoking then. Passing a fresh abort signal produces the same failure. An equivalent proxy resolving undefined from killHandler also causes a cleanup failure event.

Environment and controls

Confirmed with real workers in Chromium 155. Pre-port 05f4fb22 resolves 42 with and without a signal and accepts the cleanup proxy. Upstream poolifier a9c3ce08 also passes all three cases on Node v26.11.1: native promise branding skips prototype inspection for this proxy. No published package version was tested.

Cause and suggested fix

Browser fallback at src/worker/abstract-worker.ts:889-897 admits every object when isPromise is unavailable. Isolate optional native-promise probing so an unsuccessful probe falls back to captured-then assimilation, without suppressing actual then getter errors. Preserve single-read and original-receiver guarantees. This is not the documented modified-Promise-constructor limitation; the proxy wraps an ordinary thenable.

Activity

  1. added a commit that references this issue on Oct 18, 2026
    b4e34cb
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

    bugSomething isn't workingtriage

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions