Describe the Bug
useTool's TypeScript overload resolution falls over, with a genuinely confusing error, when a tool's run function returns different shapes from different branches. No output schema needs to be declared for this to happen.
Here's a real example, a commit_extraction tool:
useTool({
name: 'commit_extraction',
input: v.object({ /* ... */ }),
async run({ data }) {
if (alreadyDone) {
return { output: { committed: false, alreadyCommitted: true } };
}
// ...
return { output: { committed: true, factCount, summaryCount } };
},
});
TypeScript's complaint is that Promise<...> isn't assignable to Promise<void>. That's misleading. There's no output schema anywhere in sight, and nothing about the error points at what's actually wrong: the two branches don't return the same shape.
Expected Behavior
One of two things would help here:
useTool infers the run return type as the union of both branches, so shapes that differ but are each individually valid still typecheck, or
- if a single shape really is required, say so directly ("each branch of
run must return the same output shape") instead of surfacing an unrelated Promise<void> mismatch.
Steps to Reproduce
- Write a tool whose
run has two branches returning objects with different keys, like above.
- Run
tsc (or wrangler types && tsc --noEmit).
- Watch the cascading overload error blame
Promise<void>, with no mention of the actual shape mismatch.
Workaround
Made both branches return the same shape, including a boolean discriminant field (alreadyCommitted). The type error went away immediately.
Describe the Bug
useTool's TypeScript overload resolution falls over, with a genuinely confusing error, when a tool'srunfunction returns different shapes from different branches. Nooutputschema needs to be declared for this to happen.Here's a real example, a
commit_extractiontool:TypeScript's complaint is that
Promise<...>isn't assignable toPromise<void>. That's misleading. There's nooutputschema anywhere in sight, and nothing about the error points at what's actually wrong: the two branches don't return the same shape.Expected Behavior
One of two things would help here:
useToolinfers therunreturn type as the union of both branches, so shapes that differ but are each individually valid still typecheck, orrunmust return the same output shape") instead of surfacing an unrelatedPromise<void>mismatch.Steps to Reproduce
runhas two branches returning objects with different keys, like above.tsc(orwrangler types && tsc --noEmit).Promise<void>, with no mention of the actual shape mismatch.Workaround
Made both branches return the same shape, including a boolean discriminant field (
alreadyCommitted). The type error went away immediately.