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
- Create a
BatchProcessorSync or SqsFifoPartialProcessor
- Register an
async record handler that rejects for every record
- Call
processPartialResponseSync with a batch of two records
- 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
Expected Behavior
BatchProcessorSyncandSqsFifoPartialProcessoronly 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 tosuccessHandler(). When the handler isasync, that value is a pending Promise, so every record is recorded as a success,batchItemFailuresis 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 anasyncfunction here.SqsFifoPartialProcessorinherits this behaviour fromBatchProcessorSync, 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
With two records, the function returns
{ "batchItemFailures": [] },processor.successMessageshas two entries,processor.failureMessagesis empty, and two unhandled rejections are logged afterwards. The same happens withSqsFifoPartialProcessor.Steps to Reproduce
BatchProcessorSyncorSqsFifoPartialProcessorasyncrecord handler that rejects for every recordprocessPartialResponseSyncwith a batch of two recordsbatchItemFailuresand two success entries, followed by unhandled rejectionsPossible Solution
In
BatchProcessorSync.processRecordSync(), after calling the handler, check whether the result is thenable and, if so, throw aBatchProcessingErrorexplaining that the processor is synchronous and pointing toBatchProcessororSqsFifoPartialProcessorAsyncwithprocessPartialResponse. 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 howBatchProcessor.processSync()already rejects misuse.A short note in the FIFO section of the batch docs stating that
SqsFifoPartialProcessorrequires 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