diff --git a/packages/server/src/server/plugins/compiler-worker.ts b/packages/server/src/server/plugins/compiler-worker.ts index e2d271016e..ddf2544b8b 100644 --- a/packages/server/src/server/plugins/compiler-worker.ts +++ b/packages/server/src/server/plugins/compiler-worker.ts @@ -10,6 +10,7 @@ export type CompilerReply = | { kind: "error"; error: Error; + properties: Record; issues?: ZodIssue[]; errors?: unknown[]; warnings?: unknown[]; @@ -50,6 +51,9 @@ async function run(task: CompilerTask): Promise { port.postMessage({ kind: "error", error, + // Error cloning drops own metadata such as filesystem code/path. Zod's + // readonly issues are reconstructed separately by the compiler owner. + properties: Object.fromEntries(Object.entries(error).filter(([key]) => key !== "issues")), ...("issues" in error ? { issues: error.issues as ZodIssue[] } : {}), ...("errors" in error ? { errors: error.errors as unknown[] } : {}), ...("warnings" in error ? { warnings: error.warnings as unknown[] } : {}), diff --git a/packages/server/src/server/plugins/compiler.test.ts b/packages/server/src/server/plugins/compiler.test.ts index 0084ba2638..67ca96e596 100644 --- a/packages/server/src/server/plugins/compiler.test.ts +++ b/packages/server/src/server/plugins/compiler.test.ts @@ -1015,3 +1015,14 @@ it("preserves manifest validation errors across the compiler boundary", async () await writeFile(path.join(directory, "paseo-plugin.json"), "{"); await expect(compileDirectoryPlugin(directory, "0.13.2")).rejects.toBeInstanceOf(SyntaxError); }); + +it("preserves filesystem error metadata for a missing entry", async () => { + const { server } = await createSplitPlugin(); + await rm(server); + await expect(compilePlugin({ client: null, server })).rejects.toMatchObject({ + code: "ENOENT", + errno: expect.any(Number), + syscall: "realpath", + path: server, + }); +}); diff --git a/packages/server/src/server/plugins/compiler.ts b/packages/server/src/server/plugins/compiler.ts index 829acd1862..4a4197e2de 100644 --- a/packages/server/src/server/plugins/compiler.ts +++ b/packages/server/src/server/plugins/compiler.ts @@ -111,6 +111,7 @@ class PluginCompiler { resolve(reply.value as TaskResult[T["kind"]]); } else { const error = reply.issues ? new ZodError(reply.issues) : reply.error; + Object.assign(error, reply.properties); if (reply.errors) Object.assign(error, { errors: reply.errors }); if (reply.warnings) Object.assign(error, { warnings: reply.warnings }); // Promise.all can reject while the other target is still compiling.