[world-vercel] Fix run_failed event schema validation with separate resolve/lazy wire schemas#1222
Conversation
The EventResultWireSchema used the strict WorkflowRunSchema (discriminated union) which requires error: StructuredErrorSchema for status 'failed'. When run_failed events use remoteRefBehavior 'lazy', the server returns the error field as a RemoteRef or undefined, causing Zod validation to fail. Switch to WorkflowRunWireBaseSchema which accepts error as string, structured object, or undefined, and apply deserializeError() to normalize the run entity before returning it to consumers. Closes #1180
🦋 Changeset detectedLatest commit: d59a227 The changes in this PR will be included in the next version bump. This PR includes changesets to release 15 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 |
🧪 E2E Test Results❌ Some tests failed Summary
❌ Failed Tests🌍 Community Worlds (48 failed)turso (48 failed):
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
❌ 🌍 Community Worlds
✅ 📋 Other
|
📊 Benchmark Results
workflow with no steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express workflow with 1 step💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 10 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 25 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro workflow with 50 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro Promise.all with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.all with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.all with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro Promise.race with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express Promise.race with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express Promise.race with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
|
There was a problem hiding this comment.
Pull request overview
This PR updates the @workflow/world-vercel client to avoid Zod validation failures when creating run_failed events in remoteRefBehavior: 'lazy' mode, by loosening the wire-schema for the returned run and normalizing the wire error format before returning results.
Changes:
- Exported
WorkflowRunWireBaseSchemafromruns.tsso it can be reused elsewhere. - Updated
events.createresult validation to useWorkflowRunWireBaseSchemafor the optionalrunfield and applieddeserializeError()before returningrun. - Added a changeset to publish a patch release documenting the fix.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
packages/world-vercel/src/runs.ts |
Exports WorkflowRunWireBaseSchema for reuse by event result parsing. |
packages/world-vercel/src/events.ts |
Uses the wire-base run schema for event-create responses and normalizes run errors via deserializeError(). |
.changeset/fix-run-failed-schema.md |
Declares a patch release and documents the validation fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…lazy mode Split EventResultWireSchema into two variants based on remoteRefBehavior: - EventResultResolveWireSchema uses WorkflowRunSchema (discriminated union) for events where the client reads result.run (run_created, run_started, step_started), preserving type safety. - EventResultLazyWireSchema uses WorkflowRunWireBaseSchema for events where the client discards the response, allowing error to be a string or undefined in lazy ref mode.
|
Here's my analysis of PR #1222. SummaryThis PR fixes a Zod validation failure when the server returns Regression Analysis: No regressions expectedThe resolve path is behavior-identical to the old code. For events in The lazy path fixes the bug without side effects. I verified all callers of
Every lazy-path event discards the The pattern is already established. Security Analysis: No issues
VerdictClean and safe. The fix is well-scoped, follows established patterns in the same codebase, and only relaxes validation where the strict schema was provably wrong (lazy mode can't guarantee discriminated union invariants). No regressions or security concerns. |
Summary
EventResultWireSchemawas using the strictWorkflowRunSchema(discriminated union) to validate therunentity in the server response. Forstatus: 'failed', this requireserror: StructuredErrorSchema— butrun_failedevents useremoteRefBehavior: 'lazy', so the server may return the error as a string orundefined, causing Zod validation to fail.WorkflowRunWireBaseSchema(which would degrade type safety for all events), this PR splits the validation into two schemas selected byremoteRefBehavior:EventResultResolveWireSchema— usesWorkflowRunSchema(strict discriminated union) for events where the client readsresult.run(run_created,run_started,step_started). The server fully resolves refs in this mode, so type invariants always hold.EventResultLazyWireSchema— usesWorkflowRunWireBaseSchema(loose) for events where the client discards the response (run_failed,run_completed,run_cancelled, etc.).deserializeError()normalizes the wire-format error field afterward.WorkflowRunWireBaseSchemafromruns.tsso it can be imported byevents.ts.listWorkflowRunsandgetWorkflowRuninruns.ts, which select betweenWorkflowRunWireWithRefsSchemaandWorkflowRunWireSchemabased onremoteRefBehavior.