Skip to content

V8 15.6: new Function("") now allowed during requests, which breaks code-generation probes (zod v4) in deployed Workers #7740

Open

Description

@ntotten

Summary

Since the V8 15.6.75 update (3f8a3dd), new Function('') is allowed inside a request. Real code generation, like new Function('return 1'), is still blocked. Libraries probe whether code generation works by trying new Function(''). That probe now passes, so they switch to code-generating paths, and those throw EvalError: Code generation from strings disallowed for this context on the first real use.

This broke Workers that were deployed weeks or months ago, with no redeploy. It started around 2026-10-13 20:00 UTC, which we assume is when 15.6 rolled out to production.

Most visible case: zod v4

zod v4 (and the zod/v4 build in zod 3.25.x) decides once whether to JIT-compile object parsers:

// zod 3.25.27 (dist/esm/v4/core/util.js)
export const allowsEval = cached(() => {
  try {
    const F = Function;
    new F("");
    return true;
  } catch (_) {
    return false;
  }
});

Before 15.6, the probe threw, so zod used its interpreter and everything worked. On 15.6 the probe passes, zod compiles parsers with new Function(code), and every parse fails with EvalError.

Later zod versions (3.25.76 and 4.x) skip the probe when navigator.userAgent includes Cloudflare. navigator only exists from compat date 2022-03-21, though, so Workers with an older compat date or none (2021-11-01) are still affected on current zod. Workers bundling zod 3.25.27 to 3.25.75 are affected on any compat date.

Repro

export default {
  fetch() {
    let probe;
    try { new Function(""); probe = "allowed"; } catch { probe = "blocked"; }
    let real;
    try { new Function("return 1")(); real = "allowed"; } catch (e) { real = String(e); }
    return new Response(JSON.stringify({ probe, real }));
  },
};

Expected from the setup-test change in 3f8a3dd:

  • V8 15.5: {"probe":"blocked","real":"EvalError: ..."}
  • V8 15.6: {"probe":"allowed","real":"EvalError: ..."}

A minimal zod case: a schema built at module scope with zod 3.25.27, import { z } from "zod/v4", then schema.parse({...}) inside fetch.

Impact

Ask

One of:

  1. Keep new Function('') blocked wherever real code generation is blocked, so feature probes keep giving the right answer; or
  2. Put the new allowance behind a compatibility date/flag, so existing Workers keep the 15.5 behavior.

We've worked around it on our side by forcing zod's jitless mode on workerd. That only reaches Workers once they're rebuilt.

Related: #6344 (empty-function allowance), #7676.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions