denoland/deno · error · TypeError
Illegal invocation
Error message
Illegal invocation
What it means
The internal SecureContext object returned by tls.createSecureContext installs an _external property whose getter only works when called on an object branded via a private WeakSet (ext/node/polyfills/_tls_common.ts:636). Calling the getter with a tampered this — via .call(), Reflect.get with a receiver, or a detached getter — throws TypeError('Illegal invocation'), the same WebIDL-style brand check browsers use.
Source
Thrown at ext/node/polyfills/_tls_common.ts:636
ca: useDefaultCA ? undefined : normalizeCertValue(effectiveCa),
useDefaultCA,
cert: normalizeCertPem(options.cert) ?? pfxCert,
key: normalizeKeyPem(options.key, options.passphrase) ?? pfxKey,
minVersion,
maxVersion,
ciphers: options.ciphers,
passphrase: options.passphrase,
sigalgs: options.sigalgs,
ecdhCurve: options.ecdhCurve,
};
secureContextBrand.add(this.context);
ObjectDefineProperty(this.context, "_external", {
__proto__: null,
configurable: true,
enumerable: false,
get(this: object) {
if (!WeakSetPrototypeHas(secureContextBrand, this)) {
throw new TypeError("Illegal invocation");
}
return this;
},
});
(this.context as any).setOptions = function setOptions(
this: object,
_options?: number,
) {
if (!WeakSetPrototypeHas(secureContextBrand, this)) {
throw new TypeError("Illegal invocation");
}
};
(this.context as any).addCACert = function addCACert(
this: any,
cert: any,
) {
if (!WeakSetPrototypeHas(secureContextBrand, this)) {
throw new TypeError("Illegal invocation");View on GitHub (pinned to 89f33cbef2)
Solutions
- Read the property normally (ctx._external) instead of extracting the getter and changing its receiver
- If you extracted the descriptor, invoke it bound to the original object: const d = Object.getOwnPropertyDescriptor(ctx, '_external'); d.get.call(ctx)
- In mocks/spies, wrap the original object rather than re-binding its accessors to a fake this
Example fix
// before const desc = Object.getOwnPropertyDescriptor(ctx, '_external'); const ext = desc.get.call(myFakeThis); // TypeError // after const ext = ctx._external; // brand check passes
Defensive patterns
Strategy: validation
Validate before calling
// Never extract the getter; read the property on the branded object only. const ext = ctx._external;
Try / catch
try { Reflect.get(target, '_external', receiver); } catch (e) { if (e instanceof TypeError && e.message === 'Illegal invocation') receiver = target; /* retry with correct receiver */ else throw e; } Prevention
- Avoid Reflect.get with a custom receiver on foreign objects
- In mocks, wrap whole objects instead of rebinding accessors
When it happens
Trigger: Reflect.get(ctx, '_external', someOtherObject), Object.getOwnPropertyDescriptor(ctx, '_external').get.call({}), or destructuring the property off a Proxy that forwards the receiver. Normal property reads like ctx._external never throw.
Common situations: Test suites or mocking libraries (sinon, test spies) that grab property descriptors and invoke getters with a stub receiver; code that copies accessors between objects with Object.assign on the descriptor level; deep-clone tools that re-invoke getters.
Related errors
- Expected the second argument to assertSnapshot() to be an op
- Snapshot serializer must return a string
- fetch failed
- Return value from serve handler must be a response or a prom
- Unsupported 'certFile' / 'keyFile' options provided: use 'ce
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/1aa6cee88ea4f0a5.
Report an issue: GitHub.