Split out of #15123 so that PR can land. Raised by Devin: #15123 (comment)
Problem
In WorkflowBinding.deleteBatch() (packages/workflows-shared/src/binding.ts:243-309), the returned errors array can never be populated, so local dev always reports a batch delete as fully successful — including for instance IDs that do not exist. That diverges from production behaviour and hides real failures.
Three separate reasons, all verified against the current code:
1. Both branches record success (binding.ts:274-279):
try {
await stub.getStatus();
resultMap.set(id, { ok: true });
} catch {
resultMap.set(id, { ok: true });
}
The getStatus() result is discarded either way, so it is a wasted RPC round-trip. It is also being called on a stub that has just been aborted by stub.unsafeAbort() on the line above — the stub is stale at that point (compare the retry handling around binding.ts:400-421), so this call is expected to reject in the normal case.
2. The rejection sweep is unreachable (binding.ts:283-292). Every await inside the Promise.allSettled callback is already wrapped in try/catch (binding.ts:268-272 and 274-279), so the callback cannot reject and result.status === "rejected" is never true.
3. A missing result is also counted as deleted (binding.ts:297-306). The final loop's else branch pushes { id } into deleted when resultMap has no entry for the ID at all, which masks any path that failed to record a result.
Also in the same function
IDs are de-duplicated for the delete work (uniqueIds, binding.ts:258) but the result is built by iterating the original instanceIds (binding.ts:297), so a caller passing the same ID twice gets it back twice in deleted.
What needs deciding
The intended semantics, before the code can be restructured:
- Should a non-existent instance be an error entry (
not_found) or a silent success? Production behaviour should be the reference here.
- If it should be an error, existence has to be checked before
unsafeAbort(), e.g. getStatus() on a fresh stub first, since after the abort the stub cannot be used to tell.
- Should duplicate input IDs appear once or once-per-occurrence in
deleted?
Acceptance
Split out of #15123 so that PR can land. Raised by Devin: #15123 (comment)
Problem
In
WorkflowBinding.deleteBatch()(packages/workflows-shared/src/binding.ts:243-309), the returnederrorsarray can never be populated, so local dev always reports a batch delete as fully successful — including for instance IDs that do not exist. That diverges from production behaviour and hides real failures.Three separate reasons, all verified against the current code:
1. Both branches record success (
binding.ts:274-279):The
getStatus()result is discarded either way, so it is a wasted RPC round-trip. It is also being called on a stub that has just been aborted bystub.unsafeAbort()on the line above — the stub is stale at that point (compare the retry handling aroundbinding.ts:400-421), so this call is expected to reject in the normal case.2. The rejection sweep is unreachable (
binding.ts:283-292). Everyawaitinside thePromise.allSettledcallback is already wrapped intry/catch(binding.ts:268-272and274-279), so the callback cannot reject andresult.status === "rejected"is never true.3. A missing result is also counted as deleted (
binding.ts:297-306). The final loop'selsebranch pushes{ id }intodeletedwhenresultMaphas no entry for the ID at all, which masks any path that failed to record a result.Also in the same function
IDs are de-duplicated for the delete work (
uniqueIds,binding.ts:258) but the result is built by iterating the originalinstanceIds(binding.ts:297), so a caller passing the same ID twice gets it back twice indeleted.What needs deciding
The intended semantics, before the code can be restructured:
not_found) or a silent success? Production behaviour should be the reference here.unsafeAbort(), e.g.getStatus()on a fresh stub first, since after the abort the stub cannot be used to tell.deleted?Acceptance
errorsrather thandeleted.delete()anddeleteBatch()have no test coverage #15139).