From ef3ca7cc46fe657dd0e66f7f43a6fa4f7eaf2767 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 20:14:27 +0000 Subject: [PATCH] fix(world): accept lazy terminal run data (#3914) Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> Signed-off-by: Shalabh Chaturvedi --- .changeset/lazy-terminal-run-data.md | 6 ++++++ packages/core/src/runtime/run.ts | 7 ++++++- packages/world/src/runs.test.ts | 27 +++++++++++++++++++++++++++ packages/world/src/runs.ts | 4 ++-- 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 .changeset/lazy-terminal-run-data.md create mode 100644 packages/world/src/runs.test.ts diff --git a/.changeset/lazy-terminal-run-data.md b/.changeset/lazy-terminal-run-data.md new file mode 100644 index 0000000000..17ee9e1daf --- /dev/null +++ b/.changeset/lazy-terminal-run-data.md @@ -0,0 +1,6 @@ +--- +"@workflow/world": patch +"@workflow/core": patch +--- + +Accept lazy completed and failed runs whose payload is represented by a remote reference. diff --git a/packages/core/src/runtime/run.ts b/packages/core/src/runtime/run.ts index 02a6459a70..f7488766b5 100644 --- a/packages/core/src/runtime/run.ts +++ b/packages/core/src/runtime/run.ts @@ -291,7 +291,12 @@ export class Run { } if (run.status === 'failed') { - throw new WorkflowRunFailedError(this.runId, run.error); + // Terminal run data can be returned lazily, so the error payload + // may not be materialized. Fall back to a generic message. + throw new WorkflowRunFailedError( + this.runId, + run.error ?? { message: 'Workflow run failed' } + ); } throw new WorkflowRunNotCompletedError(this.runId, run.status); diff --git a/packages/world/src/runs.test.ts b/packages/world/src/runs.test.ts new file mode 100644 index 0000000000..e4d912808b --- /dev/null +++ b/packages/world/src/runs.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from 'vitest'; +import { z } from 'zod'; +import { WorkflowRunSchema } from './runs.js'; + +describe('WorkflowRunSchema', () => { + it('accepts terminal runs without materialized payloads', () => { + const completedSchema = WorkflowRunSchema.options[2]; + const failedSchema = WorkflowRunSchema.options[3]; + const run = { + runId: 'wrun_1', + deploymentId: 'dpl_1', + workflowName: 'workflow_1', + createdAt: new Date(), + updatedAt: new Date(), + completedAt: new Date(), + }; + + expect(completedSchema.shape.output).toBeInstanceOf(z.ZodOptional); + expect(failedSchema.shape.error).toBeInstanceOf(z.ZodOptional); + expect( + WorkflowRunSchema.safeParse({ ...run, status: 'completed' }).success + ).toBe(true); + expect( + WorkflowRunSchema.safeParse({ ...run, status: 'failed' }).success + ).toBe(true); + }); +}); diff --git a/packages/world/src/runs.ts b/packages/world/src/runs.ts index 248bbe1176..989c2eb021 100644 --- a/packages/world/src/runs.ts +++ b/packages/world/src/runs.ts @@ -99,7 +99,7 @@ export const WorkflowRunSchema = z.discriminatedUnion('status', [ // Completed state - output can be v1 or v2 format WorkflowRunBaseSchema.extend({ status: z.literal('completed'), - output: SerializedDataSchema, + output: SerializedDataSchema.optional(), error: z.undefined().optional(), completedAt: z.coerce.date(), }), @@ -107,7 +107,7 @@ export const WorkflowRunSchema = z.discriminatedUnion('status', [ WorkflowRunBaseSchema.extend({ status: z.literal('failed'), output: z.undefined().optional(), - error: StructuredErrorSchema, + error: StructuredErrorSchema.optional(), completedAt: z.coerce.date(), }), ]);