calls.waitForResult returns as soon as the status is terminal, without checking whether the result came with it.
while (Date.now() <= deadline) {
const call = await this.get(callId);
if (call.status === "completed" || call.status === "failed" || call.status === "canceled") {
return call;
}
await sleep(intervalMs);
}
goals.waitForResult, in the same package, does the opposite. It ignores the status and waits for the thing the caller asked for:
if (run.result !== null || run.error !== null) {
return run;
}
The goals one is right, and the calls one would also be right if terminal always meant finished. It does not always. Across five calls that each sent a result_schema, four had structured_result populated the moment the status went terminal. The fifth came back null, and reading the same call about forty seconds later, the result was there. Filed that separately as CALLE-AI/calle-docs#40.
So waitForResult sometimes returns a call with no result on a call that has one.
Reproduction
No network needed, since the client takes a fetch. This replays what the API did, with the timing compressed:
import { CalleClient } from "@call-e/calle";
let reads = 0;
const stub = async () => {
reads += 1;
const structured_result = reads === 1 ? null : { open_saturday: "yes" };
return new Response(JSON.stringify({
id: "call_x", object: "call_task", status: "completed", task: "t",
recipients: [], structured_result, summary: null, task_completed: true,
completion_confidence: { score: 0.9, label: "high" }, evidence: [],
metadata: {}, failure_code: null, failure_message: null,
created_at: "2026-09-04T20:00:00Z", completed_at: "2026-09-04T20:01:00Z",
}), { status: 200, headers: { "Content-Type": "application/json" } });
};
const client = new CalleClient({ apiKey: "x", fetch: stub });
const call = await client.calls.waitForResult("call_x", { intervalMs: 1 });
console.log(reads, call.status, call.structuredResult);
One read, terminal, no result. createAndWait inherits it, and that is the method the quickstart reaches for.
Four times out of five is the annoying frequency. A day of testing looks clean and then it drops one in production, and the report says the API returned nothing, on a call id that has a result on it by the time anybody looks.
Suggested fix
Do what the goals loop already does. When the caller sent a resultSchema and the status is terminal with structuredResult still null, keep polling for a bounded settle window before giving up on it.
Only when a schema was sent, otherwise a call that never asked for a result waits out the window for something that was never coming.
calls.waitForResultreturns as soon as the status is terminal, without checking whether the result came with it.goals.waitForResult, in the same package, does the opposite. It ignores the status and waits for the thing the caller asked for:The goals one is right, and the calls one would also be right if terminal always meant finished. It does not always. Across five calls that each sent a
result_schema, four hadstructured_resultpopulated the moment the status went terminal. The fifth came backnull, and reading the same call about forty seconds later, the result was there. Filed that separately as CALLE-AI/calle-docs#40.So
waitForResultsometimes returns a call with no result on a call that has one.Reproduction
No network needed, since the client takes a
fetch. This replays what the API did, with the timing compressed:One read, terminal, no result.
createAndWaitinherits it, and that is the method the quickstart reaches for.Four times out of five is the annoying frequency. A day of testing looks clean and then it drops one in production, and the report says the API returned nothing, on a call id that has a result on it by the time anybody looks.
Suggested fix
Do what the goals loop already does. When the caller sent a
resultSchemaand the status is terminal withstructuredResultstill null, keep polling for a bounded settle window before giving up on it.Only when a schema was sent, otherwise a call that never asked for a result waits out the window for something that was never coming.