The startup probe in unpack.js that detects whether new Function() is available can give a false positive on Cloudflare Workers, causing silent decode failures at runtime.
The probe
var inlineObjectReadThreshold = 2;
try { new Function('') } catch(error) { inlineObjectReadThreshold = Infinity }
This assumes that if new Function() works at module initialization time, it will continue to work at runtime. That assumption doesn't hold on Cloudflare Workers.
Why it fails on CF Workers
CF Workers has an allow_eval_during_startup compat flag (default since compatibility_date >= 2025-06-01) that permits new Function() during module initialization but blocks it at runtime. So:
- Module loads → startup probe succeeds →
inlineObjectReadThreshold stays at 2
- At runtime, after 4+ same-structure objects are decoded →
readObject.count++ > 2 → new Function() is called → throws
Workarounds
The msgpackr/index-no-eval entry point works, as does passing { useRecords: false }. But both require the consumer to know about this issue upfront. The probe is meant to handle this automatically and doesn't.
Suggestions
-
Document the CF Workers footgun — at minimum, note in the README that msgpackr/index-no-eval is required on CF Workers even when allow_eval_during_startup is enabled, since the startup probe can give a false positive.
-
Wrap the actual JIT attempt in a try/catch with a fallback to the slow path, so the probe is resilient to environments where new Function() availability changes between initialization and runtime:
if (readObject.count++ > inlineObjectReadThreshold) {
try {
structure.read = (new Function('r', 'return function(){return {' + ... + '}}'))(read)
} catch(e) {
inlineObjectReadThreshold = Infinity // disable JIT globally on first failure
}
}
The startup probe in
unpack.jsthat detects whethernew Function()is available can give a false positive on Cloudflare Workers, causing silent decode failures at runtime.The probe
This assumes that if
new Function()works at module initialization time, it will continue to work at runtime. That assumption doesn't hold on Cloudflare Workers.Why it fails on CF Workers
CF Workers has an
allow_eval_during_startupcompat flag (default sincecompatibility_date >= 2025-06-01) that permitsnew Function()during module initialization but blocks it at runtime. So:inlineObjectReadThresholdstays at2readObject.count++ > 2→new Function()is called → throwsWorkarounds
The
msgpackr/index-no-evalentry point works, as does passing{ useRecords: false }. But both require the consumer to know about this issue upfront. The probe is meant to handle this automatically and doesn't.Suggestions
Document the CF Workers footgun — at minimum, note in the README that
msgpackr/index-no-evalis required on CF Workers even whenallow_eval_during_startupis enabled, since the startup probe can give a false positive.Wrap the actual JIT attempt in a try/catch with a fallback to the slow path, so the probe is resilient to environments where
new Function()availability changes between initialization and runtime: