Problem
The React proxy returned by Live.use(Component, ...) only forwards the first argument to the server-side action. Any positional arguments after the first are silently dropped.
The action signature on the server still type-checks if you write async spawn(agent: string, cwd: string) — TypeScript doesn't know the proxy only forwards one arg, so the developer writes posicional code that compiles, runs, and silently misbehaves at runtime.
Reproduction
Server (LiveAgentList.ts):
export class LiveAgentList extends LiveComponent<...> {
static publicActions = ["spawn"] as const
// Looks fine. Compiles. Runs. Silently breaks at runtime.
async spawn(agent: string, cwd: string) {
console.log({ agent, cwd })
// agent === "claude --foo" ✅
// cwd === undefined ❌ ← second arg got dropped
...
}
}
Client:
const list = Live.use(LiveAgentList, {} as any)
await list.spawn("claude --foo", "/home/me/project")
// ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
// forwarded silently dropped
The server receives agent = \"claude --foo\" and cwd = undefined, returning a spurious validation error ("cwd vazio") that's hard to trace back to the real cause.
Root cause
In @fluxstack/live-react/dist/index.js around L722:
```js
return async (payload) => {
const id = componentId || lastComponentIdRef.current;
if (!id || !connected) throw new Error("Not connected");
const response = await sendMessageAndWait({
type: "CALL_ACTION",
componentId: id,
action: prop,
payload // ← only the first arg
}, 1e4);
...
};
```
And on the server (@fluxstack/live/dist/index.js L2972):
```js
const result = await method.call(component, payload); // single arg
```
So the contract is genuinely "actions take exactly one payload" — but that contract is invisible to TypeScript (the method signature on the class is whatever the developer wrote) and undocumented in the README I could find.
Why this is a footgun
- No compile-time error — the class method's signature is the source of truth for TS, so
await list.spawn(a, b) is accepted.
- No runtime error — the dropped args just become
undefined, which fails downstream with confusing messages ("cwd vazio", "id inválido", etc.).
- Easy to write —
async spawn(agent, cwd) is more idiomatic than async spawn({ agent, cwd }), so newcomers write it that way and hit the bug.
- Looks like a server bug — the developer sees "cwd is empty" on the server and chases the wrong file.
Suggested fixes (any of these would help)
- Document explicitly in the LiveComponent docs that actions receive a single object payload, with an example. (LLMD/resources/live-components.md)
- Type-level constraint: refine
publicActions typing so the proxy's call signature is (payload: Parameters<Component[A]>[0]) => ... — that already constrains it to one argument, but ideally the method declaration itself should be constrained to one-arg via a helper like:
```ts
type LiveAction<P, R> = (payload: P) => Promise
```
and require actions to be declared as spawn: LiveAction<{ agent: string; cwd: string }, ...>.
- Dev-mode warning: when the React proxy is invoked with
arguments.length > 1, console.warn a clear message: \"LiveComponent action '<name>' was called with N positional args but only the first is forwarded — actions take a single payload object. Did you mean .spawn({ agent, cwd })?\". Zero runtime cost in prod, catches the bug immediately in dev.
Option 3 is the lowest-risk and would have saved me a debugging session today. Happy to send a PR if you like the approach.
Environment
@fluxstack/live 0.8.0
@fluxstack/live-react 0.8.0
@fluxstack/live-client 0.8.0
Problem
The React proxy returned by
Live.use(Component, ...)only forwards the first argument to the server-side action. Any positional arguments after the first are silently dropped.The action signature on the server still type-checks if you write
async spawn(agent: string, cwd: string)— TypeScript doesn't know the proxy only forwards one arg, so the developer writes posicional code that compiles, runs, and silently misbehaves at runtime.Reproduction
Server (
LiveAgentList.ts):Client:
The server receives
agent = \"claude --foo\"andcwd = undefined, returning a spurious validation error ("cwd vazio") that's hard to trace back to the real cause.Root cause
In
@fluxstack/live-react/dist/index.jsaround L722:```js
return async (payload) => {
const id = componentId || lastComponentIdRef.current;
if (!id || !connected) throw new Error("Not connected");
const response = await sendMessageAndWait({
type: "CALL_ACTION",
componentId: id,
action: prop,
payload // ← only the first arg
}, 1e4);
...
};
```
And on the server (
@fluxstack/live/dist/index.jsL2972):```js
const result = await method.call(component, payload); // single arg
```
So the contract is genuinely "actions take exactly one payload" — but that contract is invisible to TypeScript (the method signature on the class is whatever the developer wrote) and undocumented in the README I could find.
Why this is a footgun
await list.spawn(a, b)is accepted.undefined, which fails downstream with confusing messages ("cwd vazio", "id inválido", etc.).async spawn(agent, cwd)is more idiomatic thanasync spawn({ agent, cwd }), so newcomers write it that way and hit the bug.Suggested fixes (any of these would help)
publicActionstyping so the proxy's call signature is(payload: Parameters<Component[A]>[0]) => ...— that already constrains it to one argument, but ideally the method declaration itself should be constrained to one-arg via a helper like:```ts
type LiveAction<P, R> = (payload: P) => Promise
```
and require actions to be declared as
spawn: LiveAction<{ agent: string; cwd: string }, ...>.arguments.length > 1,console.warna clear message:\"LiveComponent action '<name>' was called with N positional args but only the first is forwarded — actions take a single payload object. Did you mean .spawn({ agent, cwd })?\". Zero runtime cost in prod, catches the bug immediately in dev.Option 3 is the lowest-risk and would have saved me a debugging session today. Happy to send a PR if you like the approach.
Environment
@fluxstack/live0.8.0@fluxstack/live-react0.8.0@fluxstack/live-client0.8.0