diff --git a/.changeset/run-metadata-json-string.md b/.changeset/run-metadata-json-string.md new file mode 100644 index 00000000000..bf9b228bc21 --- /dev/null +++ b/.changeset/run-metadata-json-string.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/core": patch +--- + +`metadata.refresh()` now works against deployments where `GET /api/v1/runs/:runId/metadata` returns the run metadata as a JSON string rather than an object. The response schema parses a string payload before validating, so `refresh()` resolves with the same metadata record on every version. diff --git a/packages/core/src/v3/schemas/api.ts b/packages/core/src/v3/schemas/api.ts index 9ec3cb24b84..fd0601fe5e7 100644 --- a/packages/core/src/v3/schemas/api.ts +++ b/packages/core/src/v3/schemas/api.ts @@ -1769,7 +1769,20 @@ export const UpdateMetadataRequestBody = FlushedRunMetadata; export type UpdateMetadataRequestBody = z.infer; export const UpdateMetadataResponseBody = z.object({ - metadata: z.record(z.string(), DeserializedJsonSchema), + metadata: z.preprocess( + (value) => { + if (typeof value !== "string") { + return value; + } + + try { + return JSON.parse(value); + } catch { + return value; + } + }, + z.record(z.string(), DeserializedJsonSchema) + ), }); export type UpdateMetadataResponseBody = z.infer; diff --git a/packages/core/src/v3/schemas/runMetadata.test.ts b/packages/core/src/v3/schemas/runMetadata.test.ts new file mode 100644 index 00000000000..c7e7e79327d --- /dev/null +++ b/packages/core/src/v3/schemas/runMetadata.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; +import { UpdateMetadataResponseBody } from "./api.js"; + +describe("UpdateMetadataResponseBody", () => { + it("parses an object metadata payload", () => { + const result = UpdateMetadataResponseBody.safeParse({ + metadata: { key: "value", count: 2 }, + }); + + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.metadata).toEqual({ key: "value", count: 2 }); + } + }); + + it("parses a metadata payload returned as a JSON string", () => { + const result = UpdateMetadataResponseBody.safeParse({ + metadata: '{"key":"value","count":2}', + }); + + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.metadata).toEqual({ key: "value", count: 2 }); + } + }); + + it("rejects a metadata payload that is neither object nor valid JSON", () => { + const result = UpdateMetadataResponseBody.safeParse({ metadata: "not-json" }); + + expect(result.success).toBe(false); + }); +});