Can't reference complex object in server action closure

Can any RSC wizards help me out here? This is very confusing to me, if i change tableSchema to be a primitive it works fine. My guess is that all of the values in the closure are serialized and sent to the client only to be sent back to the server?? If this is indeed what is happening (the error message sucks), where can i learn more about this?
// server-component.tsx
import { createInsertSchema } from "drizzle-zod";
import { posts } from "~/server/db/schema";

export async function ServerComponent() {
const tableSchema = createInsertSchema(posts);

async function action(formData: FormData) {
"use server";
console.log(tableSchema);
// ^^^^ Causes error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components.
// error goes away when commented out
console.log(formData);
}

return (
<div>
{Object.keys(tableSchema.shape).map((key) => (
<div key={key}>{key}</div>
))}
</div>
);
}
// server-component.tsx
import { createInsertSchema } from "drizzle-zod";
import { posts } from "~/server/db/schema";

export async function ServerComponent() {
const tableSchema = createInsertSchema(posts);

async function action(formData: FormData) {
"use server";
console.log(tableSchema);
// ^^^^ Causes error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components.
// error goes away when commented out
console.log(formData);
}

return (
<div>
{Object.keys(tableSchema.shape).map((key) => (
<div key={key}>{key}</div>
))}
</div>
);
}
4 Replies
JP
JP6mo ago
Wait is this what taint is for? that seems to resolve my error... nevermind... taint doesn't work.
iDarkLightning
iDarkLightning6mo ago
it's hard to tell exactly without seeing how you are calling the action, but I think you're pretty spot on? what is it that you want to learn more about? non serializble objects cannot cross the server client boundary
JP
JP6mo ago
I guess i'm trying to better understand why we have send variables being "closed over" to the client in the first place. My guess is that it's necessary to rebuild the correct context. Why couldn't react just render the RSC again to know the context (im fuzzy on this)? I guess the bigger question i have is why don't server components rerender
iDarkLightning
iDarkLightning6mo ago
Server Actions essentially are pulled out into separate route handlers, that are then called through forms our client side mutations. When you make a request to your server, the server components render the initial html, including all client components. The generated HTML is sent to the client on initial load. Then, react hydrates all of the client components with additional JS. When you make the request to your server action, react needs to make a fetch request to the route handler you've created with all of the data it needs