Add first-class serialization for built-in Error subclasses#1511
Conversation
🦋 Changeset detectedLatest commit: 4a02f24 The changes in this PR will be included in the next version bump. This PR includes changesets to release 17 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📊 Benchmark Results
workflow with no steps💻 Local Development
workflow with 1 step💻 Local Development
workflow with 10 sequential steps💻 Local Development
workflow with 25 sequential steps💻 Local Development
workflow with 50 sequential steps💻 Local Development
Promise.all with 10 concurrent steps💻 Local Development
Promise.all with 25 concurrent steps💻 Local Development
Promise.all with 50 concurrent steps💻 Local Development
Promise.race with 10 concurrent steps💻 Local Development
Promise.race with 25 concurrent steps💻 Local Development
Promise.race with 50 concurrent steps💻 Local Development
workflow with 10 sequential data payload steps (10KB)💻 Local Development
workflow with 25 sequential data payload steps (10KB)💻 Local Development
workflow with 50 sequential data payload steps (10KB)💻 Local Development
workflow with 10 concurrent data payload steps (10KB)💻 Local Development
workflow with 25 concurrent data payload steps (10KB)💻 Local Development
workflow with 50 concurrent data payload steps (10KB)💻 Local Development
Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
stream pipeline with 5 transform steps (1MB)💻 Local Development
10 parallel streams (1MB each)💻 Local Development
fan-out fan-in 10 streams (1MB each)💻 Local Development
SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
❌ Some benchmark jobs failed:
Check the workflow run for details. |
🧪 E2E Test Results✅ All tests passed Summary
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
✅ 📋 Other
|
There was a problem hiding this comment.
Pull request overview
This PR improves @workflow/core’s devalue-based serialization so built-in JavaScript Error subclasses round-trip with their original class identity (and preserves cause chains), which is foundational for the broader error-handling improvements planned in the PR series.
Changes:
- Add dedicated reducers/revivers for built-in
Errorsubclasses (TypeError,RangeError,SyntaxError,URIError,ReferenceError,EvalError,AggregateError) pluscausepreservation. - Add
Errorto theSerializableunion and expandSerializableSpecialtyping for error payloads. - Add a new test suite covering subclass round-trips,
causechains,AggregateError.errors, cross-VM behavior, and serialization key selection.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| packages/core/src/serialization.ts | Introduces subclass-aware Error reducers/revivers and adds cause handling. |
| packages/core/src/serialization.test.ts | Adds tests for built-in Error subclass round-tripping, causes, AggregateError, and cross-VM behavior. |
| packages/core/src/schemas.ts | Extends Serializable to include Error. |
| .changeset/error-subclass-serialization.md | Declares a patch release for the new error subclass serialization behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Addressed the review feedback in bfd0c99: cause serialization/deserialization (comments on lines 826 and 1182): Fixed. Reducers now only include undefined cause test (comment on line 3447): Fixed. Renamed the test and added assertion name preservation on subclasses (comment on line 794): Intentionally not changing this. Mutating |
Replace the generic Error reducer/reviver with specific handlers for TypeError, RangeError, SyntaxError, URIError, ReferenceError, EvalError, and AggregateError. Preserve the cause property on all Error types, only serializing it when present to maintain absence vs presence semantics.
Extract repetitive reducer/reviver logic into makeErrorSubclassReducer and makeErrorSubclassReviver helpers. Reduces ~120 lines of duplicated code to ~50 while preserving identical behavior. AggregateError remains as a wrapping extension since it needs to preserve the errors array.
Adding new serialization support for built-in Error subclasses is a feature, not a bug fix.
Summary
Errorreducer/reviver in the devalue serialization pipeline with specific handlers for each built-in JavaScript Error subclass:TypeError,RangeError,SyntaxError,URIError,ReferenceError,EvalError, andAggregateErrorcauseproperty on all Error types (including nested cause chains)Errorto theSerializabletype unioncausechains,AggregateError.errors, cross-VM boundaries, and serialization key verificationContext
This is PR 1 of 5 in a series to improve error handling in Workflow DevKit. The full series:
DOMExceptionserde supportWORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZEforFatalErrorandRetryableErrorWhat changed
Previously, all Error instances were serialized to
{ name, message, stack }and deserialized back as a plainError— losing the original class identity. Now:new TypeError("bad arg")round-trips as aTypeErrornew AggregateError([...], "msg")preserves itserrorsarraynew Error("x", { cause: new TypeError("y") })preserves the full cause chainWORKFLOW_SERIALIZE) still fall through to the baseErrorreducer, preservingname— same as beforeInstance/Classreducers remain first, so custom error subclasses withWORKFLOW_SERIALIZEstill take priorityCross-VM safety is maintained:
types.isNativeError()gates all Error reducers, andvalue.constructor?.namedistinguishes subclasses (works across VM boundaries whereinstanceoffails).Test plan
built-in Error subclass serializationdescribe blocktsc