Skip to content

Commit 1906c00

Browse files
committed
fix(core): use zod/v4 for the snapshot route schema so warm starts work on zod 3 projects
The snapshot route schema was created with the Zod 3 API and composed into Zod 4 objects (DequeuedMessage and the worker attempt request bodies). When a project resolves "zod" to a 3.x release, Zod 4 rejects the composed schema at parse time, so the runner's warm-start client threw on every dispatched run and exited. The run then waited for the heartbeat redrive before starting cold. Extends the Zod 3 root compatibility test to cover these schemas.
1 parent 562c943 commit 1906c00

3 files changed

Lines changed: 85 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/core": patch
3+
---
4+
5+
Fixes warm starts silently failing for deployments built with 4.6.0 to 4.6.3 in projects that resolve `zod` to a 3.x release. The runner could not parse the run handed to it by the warm-start service and exited, leaving the run waiting until the platform redrove it a few minutes later and started it cold. Redeploy to pick up the fix.

‎packages/core/src/v3/schemas/schemaCompositionCompatibility.test.ts‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,83 @@ describe("schema composition compatibility", () => {
9191
resource: { id: "my-webhook" },
9292
});
9393
});
94+
95+
it.each([
96+
{ name: "zod", v3: require.resolve("zod/v3"), v4: require.resolve("zod/v4") },
97+
{
98+
name: "zod-v3-floor",
99+
v3: require.resolve("zod-v3-floor/v3"),
100+
v4: require.resolve("zod-v3-floor/v4"),
101+
},
102+
])("parses run engine worker schemas with $name and a Zod 3 root", async ({ v3, v4 }) => {
103+
// The warm-start client parses DequeuedMessage inside the deployed runner, where the root
104+
// "zod" import resolves to whatever the user's project installed. A leaf schema built on the
105+
// Zod 3 API and composed into a Zod 4 object throws on every parse, not just on bad input.
106+
const result = await build({
107+
stdin: {
108+
contents: `
109+
import { DequeuedMessage } from "./src/v3/schemas/runEngine.ts";
110+
import { WorkerApiRunAttemptStartRequestBody } from "./src/v3/runEngineWorker/supervisor/schemas.ts";
111+
112+
const snapshotRoute = { version: 1, residency: "postgres", organizationId: "org_1" };
113+
114+
export const parsed = {
115+
dequeued: DequeuedMessage.parse({
116+
version: "1",
117+
snapshotRoute,
118+
dequeuedAt: "2026-01-01T00:00:00.000Z",
119+
snapshot: {
120+
id: "snapshot_1",
121+
friendlyId: "snapshot_1",
122+
executionStatus: "PENDING_EXECUTING",
123+
description: "Run was dequeued for execution",
124+
createdAt: "2026-01-01T00:00:00.000Z",
125+
},
126+
completedWaitpoints: [],
127+
backgroundWorker: { id: "worker_1", friendlyId: "worker_1", version: "20260101.1" },
128+
deployment: { id: "deployment_1", friendlyId: "deployment_1" },
129+
run: {
130+
id: "run_1",
131+
friendlyId: "run_1",
132+
isTest: false,
133+
machine: { name: "small-1x", cpu: 0.5, memory: 0.5, centsPerMs: 0 },
134+
attemptNumber: 1,
135+
masterQueue: "main",
136+
traceContext: {},
137+
},
138+
environment: { id: "env_1", type: "PRODUCTION" },
139+
organization: { id: "org_1" },
140+
project: { id: "proj_1" },
141+
}),
142+
attemptStart: WorkerApiRunAttemptStartRequestBody.parse({ isWarmStart: true, snapshotRoute }),
143+
};
144+
`,
145+
loader: "ts",
146+
resolveDir: packageRoot,
147+
},
148+
bundle: true,
149+
format: "esm",
150+
platform: "node",
151+
target: "node20",
152+
write: false,
153+
plugins: [
154+
{
155+
name: "resolve-root-zod-to-v3",
156+
setup(build) {
157+
build.onResolve({ filter: /^zod$/ }, () => ({ path: v3 }));
158+
build.onResolve({ filter: /^zod\/v4$/ }, () => ({ path: v4 }));
159+
},
160+
},
161+
],
162+
});
163+
164+
const bundledModule = await import(
165+
`data:text/javascript;base64,${Buffer.from(result.outputFiles[0]!.text).toString("base64")}`
166+
);
167+
168+
expect(bundledModule.parsed).toMatchObject({
169+
dequeued: { run: { id: "run_1" }, snapshotRoute: { residency: "postgres" } },
170+
attemptStart: { isWarmStart: true, snapshotRoute: { residency: "postgres" } },
171+
});
172+
});
94173
});

‎packages/core/src/v3/schemas/snapshotRoute.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { z } from "zod";
1+
import { z } from "zod/v4";
22

33
/**
44
* A run's versioned storage route (control metadata, never a TRES column), stamped on the queue message

0 commit comments

Comments
 (0)