denoland/deno · error · TypeError

Illegal invocation

Error message

Illegal invocation

What it means

Deno implements `WorkerLocation` (the class of the `location` global inside Web Workers) by storing each instance's parsed URL in the module-private `workerLocationUrls` WeakMap. The prototype getter for `hash` (ext/web/12_location.js:243-251) does `WeakMapPrototypeGet(workerLocationUrls, this)` and throws `TypeError: Illegal invocation` when the lookup returns null — i.e., when the accessor runs with a receiver that is not a genuine `WorkerLocation` instance. This is Deno's version of the brand check browsers perform on WebIDL accessors.

Source

Thrown at ext/web/12_location.js:248

class WorkerLocation {
  constructor(href = null, key = null) {
    if (key != locationConstructorKey) {
      throw new TypeError("Illegal constructor");
    }
    const url = new URL(href);
    url.username = "";
    url.password = "";
    WeakMapPrototypeSet(workerLocationUrls, this, url);
  }
}

ObjectDefineProperties(WorkerLocation.prototype, {
  hash: {
    __proto__: null,
    get() {
      const url = WeakMapPrototypeGet(workerLocationUrls, this);
      if (url == null) {
        throw new TypeError("Illegal invocation");
      }
      return url.hash;
    },
    configurable: true,
    enumerable: true,
  },
  host: {
    __proto__: null,
    get() {
      const url = WeakMapPrototypeGet(workerLocationUrls, this);
      if (url == null) {
        throw new TypeError("Illegal invocation");
      }
      return url.host;
    },
    configurable: true,
    enumerable: true,
  },

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Access the property on the real instance: `location.hash` (or `self.location.hash` inside the worker).
  2. If you extracted the getter, invoke it bound: `get.call(location)` or `Reflect.apply(get, location, [])`.
  3. Stop fabricating receivers — the private WeakMap rejects any object that was not constructed by Deno internals, by design.
  4. In generic serializer/polyfill code, read values off instances (`location.hash`) instead of re-invoking prototype accessors.

Example fix

// before
const getHash = Object.getOwnPropertyDescriptor(WorkerLocation.prototype, 'hash').get;
const h = getHash.call(myFakeLocation); // TypeError: Illegal invocation
// after
const h = location.hash; // read via the real global instance
Defensive patterns

Strategy: type-guard

Validate before calling

// Always go through the real instance from the global scope
const loc = typeof location !== 'undefined' ? location : null;
const hash = loc?.hash; // fine: getter invoked with this === loc
// For descriptor-driven code, brand-check the receiver first:
const isReal = loc != null && typeof WorkerLocation !== 'undefined' && loc instanceof WorkerLocation;

Type guard

function isWorkerLocation(obj) {
  return typeof WorkerLocation !== 'undefined' && obj instanceof WorkerLocation;
}

Prevention

When it happens

Trigger: `Object.getOwnPropertyDescriptor(WorkerLocation.prototype, 'hash').get.call(fakeObj)`, `Object.create(WorkerLocation.prototype).hash`, or any code path that invokes the `hash` accessor with a receiver other than the real `location` instance. Normal `location.hash` reads inside a worker never throw.

Common situations: Generic serializers/inspectors that walk prototypes and re-invoke accessors; duck-typing or cloning attempts with `Object.create(WorkerLocation.prototype)`; test doubles built by subclassing; deep-clone or polyfill utilities that copy property descriptors.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/7087dd9d0cf51337. Report an issue: GitHub.