Skip to content

[workflows] Local deleteBatch() always reports success, so failures are invisible #15138

Description

@petebacondarwin

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    product:workflowsRelating to Cloudflare Workflows: https://developers.cloudflare.com/workflows/

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions