Skip to content

Bug: synchronous batch processors treat a Promise-returning handler as success #5627

Description

@svozza

Expected Behavior

BatchProcessorSync and SqsFifoPartialProcessor only support synchronous record handlers. If a handler returns a Promise, the processor should fail with a clear error rather than reporting the records as processed, because it has no way to observe the Promise's outcome.

Current Behavior

processRecordSync() passes whatever the handler returns straight to successHandler(). When the handler is async, that value is a pending Promise, so every record is recorded as a success, batchItemFailures is empty, and the event source deletes the messages. Any rejection surfaces later as an unhandled rejection, after the response has already been returned.

The handler type is CallableFunction, so TypeScript does not reject an async function here. SqsFifoPartialProcessor inherits this behaviour from BatchProcessorSync, so it affects a non-deprecated class as well as the deprecated one. There is no runtime check for a thenable result anywhere in the package.

Code snippet

import {
  BatchProcessorSync,
  EventType,
  processPartialResponseSync,
} from '@aws-lambda-powertools/batch';
import type { SQSEvent, SQSRecord } from 'aws-lambda';

const processor = new BatchProcessorSync(EventType.SQS);

const recordHandler = async (_record: SQSRecord) => {
  throw new Error('failed');
};

export const handler = async (event: SQSEvent, context: unknown) =>
  processPartialResponseSync(event, recordHandler, processor, { context });

With two records, the function returns { "batchItemFailures": [] }, processor.successMessages has two entries, processor.failureMessages is empty, and two unhandled rejections are logged afterwards. The same happens with SqsFifoPartialProcessor.

Steps to Reproduce

  1. Create a BatchProcessorSync or SqsFifoPartialProcessor
  2. Register an async record handler that rejects for every record
  3. Call processPartialResponseSync with a batch of two records
  4. Observe an empty batchItemFailures and two success entries, followed by unhandled rejections

Possible Solution

In BatchProcessorSync.processRecordSync(), after calling the handler, check whether the result is thenable and, if so, throw a BatchProcessingError explaining that the processor is synchronous and pointing to BatchProcessor or SqsFifoPartialProcessorAsync with processPartialResponse. Throwing from the record loop fails the whole invocation, which is the right outcome for a programming error rather than a per-record failure, and mirrors how BatchProcessor.processSync() already rejects misuse.

A short note in the FIFO section of the batch docs stating that SqsFifoPartialProcessor requires a synchronous handler would help, since the docs no longer show the sync classes elsewhere.

Powertools for AWS Lambda (TypeScript) version

latest

AWS Lambda function runtime

24.x

Packaging format used

npm

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions