SIGN IN SIGN UP

fix(batch): propagate timeout scheduling errors so source events retry (#4749)

## Description

Propagate in-memory batch scheduling failures back to the event
subscriber so the source event is NACKed and retried. This is a
regression from the switch to in-memory buffering.

### Behavior before in-memory buffering

Before #3616, a scheduling failure propagated from
`AppendAndScheduleBatch` back to the pubsub subscriber:

1. For a new batch, `AppendAndScheduleBatch` called `ScheduleExecution`
to enqueue the batch execution job at the configured timeout and
returned any enqueue error:
https://github.com/inngest/inngest/blob/a5db9793362fc198bcfcb68c71d458ba825e9e6b/pkg/execution/executor/executor.go#L4690-L4703
2. `initialize` wrapped and returned the error from
`AppendAndScheduleBatch`:
https://github.com/inngest/inngest/blob/a5db9793362fc198bcfcb68c71d458ba825e9e6b/pkg/execution/runner/runner.go#L620
3. `functions` added the initialization error to its returned errors:
https://github.com/inngest/inngest/blob/a5db9793362fc198bcfcb68c71d458ba825e9e6b/pkg/execution/runner/runner.go#L522-L529
4. `handleMessage` added the error from `functions` to its returned
errors and returned them to the pubsub subscriber:
https://github.com/inngest/inngest/blob/a5db9793362fc198bcfcb68c71d458ba825e9e6b/pkg/execution/runner/runner.go#L336-L339
5. The pubsub broker NACKed the message on GCP Pub/Sub:
https://github.com/inngest/inngest/blob/a5db9793362fc198bcfcb68c71d458ba825e9e6b/pkg/pubsub/broker.go#L169-L176

The retry was safe because #3590 made a duplicate first event return
`BatchNew`, causing another idempotent scheduling attempt.

### Regression introduced by in-memory buffering

#3616 enabled in-memory buffering by default and delegated `Append` to
the buffer:
https://github.com/inngest/inngest/blob/f97285c9e73ce2a60078231e317f4a0161ce3090/pkg/execution/batch/redis.go#L72-L94

The same scheduling failure then stopped inside the buffer:

1. After `BulkAppend` succeeded, `flush` called `handleScheduling`:
https://github.com/inngest/inngest/blob/f97285c9e73ce2a60078231e317f4a0161ce3090/pkg/execution/batch/buffer.go#L319
2. `handleScheduling` called `ScheduleExecution`, but a failure was only
logged and metered because the function did not return an error:
https://github.com/inngest/inngest/blob/f97285c9e73ce2a60078231e317f4a0161ce3090/pkg/execution/batch/buffer.go#L367-L417
3. Although `flush` called both `BulkAppend` and `handleScheduling`,
only `BulkAppend` returned an error. `flush` could therefore only set
`pending.err` from a `BulkAppend` failure; a scheduling failure still
produced a successful pending result:
https://github.com/inngest/inngest/blob/f97285c9e73ce2a60078231e317f4a0161ce3090/pkg/execution/batch/buffer.go#L327
4. The successful result was mapped to `BatchAppend` so the executor
would not schedule the batch again:
https://github.com/inngest/inngest/blob/f97285c9e73ce2a60078231e317f4a0161ce3090/pkg/execution/batch/buffer.go#L353-L364
5. `AppendAndScheduleBatch` received `BatchAppend, nil` and took its
no-op branch:
https://github.com/inngest/inngest/blob/f97285c9e73ce2a60078231e317f4a0161ce3090/pkg/execution/executor/executor.go#L4668
and
https://github.com/inngest/inngest/blob/f97285c9e73ce2a60078231e317f4a0161ce3090/pkg/execution/executor/executor.go#L4681-L4682
6. With no error reaching `handleMessage`, the pubsub broker ACKed the
message even though no batch execution job had been enqueued.

### Fix

This change restores the original error propagation for in-memory
buffered appends:

- `scheduleBatchExecution` returns queue scheduling failures:
https://github.com/inngest/inngest/blob/d5bb35b90cc078bc1107103bf12d16d379e97c85/pkg/execution/batch/buffer.go#L518
- `handleScheduling` propagates those failures to `flush`:
https://github.com/inngest/inngest/blob/d5bb35b90cc078bc1107103bf12d16d379e97c85/pkg/execution/batch/buffer.go#L447
- `flush` sets the error on every affected pending append before
notifying its waiter:
https://github.com/inngest/inngest/blob/d5bb35b90cc078bc1107103bf12d16d379e97c85/pkg/execution/batch/buffer.go#L364-L370
- `buffer.append` returns the error to `AppendAndScheduleBatch`:
https://github.com/inngest/inngest/blob/d5bb35b90cc078bc1107103bf12d16d379e97c85/pkg/execution/batch/buffer.go#L203-L206
- `AppendAndScheduleBatch` returns the error into the existing
propagation path to `handleMessage`, causing NACK and redelivery on GCP
Pub/Sub:
https://github.com/inngest/inngest/blob/d5bb35b90cc078bc1107103bf12d16d379e97c85/pkg/execution/executor/executor.go#L5597-L5603

The successful path still returns `BatchAppend` so scheduling remains
owned by the buffer. On retry after a scheduling failure, the existing
duplicate handling added in #3675 performs another idempotent scheduling
attempt.

## Testing

- Updated the scheduling log test to assert that the scheduling error is
returned.
- Added coverage verifying that `flush` returns a scheduling failure to
pending append callers.

## Release note

Fixed an issue where transient scheduling failures could prevent a new
batch's timeout job from being enqueued, causing the batch to remain
pending past its configured timeout until it reached its batch limit.

## Migration note

None.
A
albertchae committed
a5133b03f4b109b2a50e205097b4dcd2b995cc34
Parent: 8af0ec2
Committed by GitHub <noreply@github.com> on 8/14/2026, 6:11:04 PM