Skip to content

@vercel/sandbox breaks the Next build: SWC plugin emits AnonymousClass.prototype[...] for the SDK's anonymous class expressions #3929

Closed

Description

@luca-primefold

TL;DR

@vercel/sandbox ships "use step" directives in its published dist. When a Next app using @workflow/next has the SDK anywhere in the module graph of /.well-known/workflow/v1/step, the workflow SWC plugin rewrites the SDK's classes — which are anonymous class expressions — and emits a reference to a binding that does not exist. The build fails during "Collecting page data".

Both packages are Vercel's, so this looks like an interop defect rather than a misuse.

Error

Error: Failed to collect configuration for /.well-known/workflow/v1/step
    at ignore-listed frames {
  [cause]: ReferenceError: AnonymousClass is not defined
      at ../../node_modules/@vercel/sandbox/dist/filesystem.js (app/.well-known/workflow/v1/step/route.js:6207:84)
      at <unknown> (app/.well-known/workflow/v1/step/route.js:10:54)
      at module evaluation (app/.well-known/workflow/v1/step/route.js:11749:1)
    6205 |       }
    6206 |     };
  > 6207 |     registerStepFunction29("step//@vercel/sandbox@3.1.0//AnonymousClass#readFile", AnonymousClass.prototype["readFile"]);
         |                                                                                    ^
    6208 |     registerStepFunction29("step//@vercel/sandbox@3.1.0//AnonymousClass#writeFile", AnonymousClass.prototype["writeFile"]);
    6209 |     registerStepFunction29("step//@vercel/sandbox@3.1.0//AnonymousClass#appendFile", AnonymousClass.prototype["appendFile"]);
    6210 |     registerStepFunction29("step//@vercel/sandbox@3.1.0//AnonymousClass#mkdir", AnonymousClass.prototype["mkdir"]);
}

> Build error occurred
Error: Failed to collect page data for /.well-known/workflow/v1/step

Suspected cause

@vercel/sandbox/dist/filesystem.js declares its classes as anonymous class expressions assigned to a variable:

var SandboxStats  = class { … };
var SandboxDirent = class { … };
var FileSystem    = class { … };   // 20 "use step" directives in this file

The loader transforms any file whose source carries a directive, including files under node_modules (@workflow/next/dist/loader.js, patterns.hasDirective). For a class expression the plugin appears to fall back to the placeholder name AnonymousClass instead of the variable the class is assigned to, then emits AnonymousClass.prototype["readFile"]. Nothing declares AnonymousClass, so the module throws on evaluation.

Using the assigned variable name (FileSystem), or skipping registration when no binding can be resolved, would both avoid this.

Reproduction

In a Next app with withWorkflow configured, import @vercel/sandbox from any module reachable from a "use step" function, then run next build. The failure is at "Collecting page data", not at compile.

Environment

  • @vercel/sandbox 3.1.0
  • @workflow/next 4.1.2
  • next 16.3.1, Turbopack
  • Node 24, pnpm 10.32.1, macOS arm64

Already ruled out

  1. Upgrading the SDK. 3.0.1, 3.1.0, 3.2.0 and 3.2.1 all ship var FileSystem = class — verified by unpacking each tarball.
  2. serverExternalPackages: ["@vercel/sandbox"]. Same failure; the loader rewrites the source before externalisation applies.
  3. A non-literal import specifier, to keep the SDK out of the graph. Turbopack folds the constant, then fails Module not found: Can't resolve '@vercel/sandbox'.
  4. A withWorkflow exclusion option. None found — the only documented option is workflows.local.port.

Questions

  1. Is a fix planned, and on which side — the SWC plugin's naming of class expressions, or the SDK's published dist shape?
  2. Is there a supported way to exclude a package from the workflow step transform?

Impact

We can only reach @vercel/sandbox from non-Next runtimes today; our Next app's workflow routes cannot use it at all. That blocks moving our sandbox workload onto Vercel Sandbox, since most of our code-execution entrypoints run inside workflow steps.

Possibly related, though both are different failure modes: #3051 (directive-free modules sent through SWC on the Nitro builder) and #3900 (valid use step directives missed after raw backticks). Both suggest the directive-detection and transform path is where this belongs.

Activity

  1. luca-primefold commented on Sep 4, 2026

    @luca-primefold
    Author

    Vercel Support routed this here rather than answering it. I opened a support case for the same defect and it was closed as out of paid-support scope, because @workflow/next is open source, with this issue named as "the best place to continue the conversation and receive further support" and a note that it was "shared internally with our engineering team for visibility" — explicitly with no ETA. So this issue is the only channel; flagging that in case triage labels are what it is waiting on.

    Question 2 above is the one that decides what we build, so narrowing it: is there any supported way to keep a node_modules package out of the step transform — a withWorkflow option, an env var, or a convention the loader honours? A plain "no" is a genuinely useful answer. We would then put the SDK behind an HTTP boundary so it is only ever loaded in a non-Next runtime, and I would rather not build that if an exclusion mechanism is on the way.

    Offering the work either way: patterns.hasDirective in @workflow/next/dist/loader.js looks like the single place a package-level skip would go, and resolving a class expression to its assigned variable name (FileSystem) instead of the AnonymousClass placeholder looks similarly contained in the plugin. Happy to open a PR against whichever side you would take.

  2. TooTallNate commented on Sep 4, 2026

    @TooTallNate
    Member

    Hey Luca, so this seems to be a regression in either the Workflow SDK compiler or @vercel/sandbox, not sure which yet, but the "use step" directives are intentionally in the sandbox package and are meant to be transformed by the Workflow SDK compiler, so skip is not the correct solution - resolving to the assigned variable name sounds more reasonable. I'll be looking into this today, but also happy to take a look at a PR if you get to it before me.

  3. TooTallNate commented on Sep 4, 2026

    @TooTallNate
    Member

    @luca-primefold Can you provide a repro repository? I am having trouble replicating your error.

  4. TooTallNate commented on Sep 10, 2026

    @TooTallNate
    Member

    PR #3971 added hardening for classes tangentially related to this issue, but since the trigger is via an override with a old version of the @workflow/builders package and it does not reproduce against latest stable, then I think we can close this.

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