Repository navigation
Bug: Base64Encoded lets malformed base64 escape safeParse and leaks an unhandled rejection #5651
Description
Expected Behavior
safeParse should never throw. A Kinesis record whose kinesis.data is not valid base64 should produce { success: false, error: ParseError, originalEvent } from KinesisDataStreamSchema.safeParse, KinesisEnvelope.safeParse, parse(event, undefined, KinesisDataStreamSchema, true), and from the decorator or Middy middleware in safeParse mode. The same applies to any customer schema built with the Base64Encoded helper.
Current Behavior
Base64Encoded is z.string().transform(...) with no base64 validation, and the transform calls fromBase64 outside a try. fromBase64 throws a TypeError for bad padding or invalid characters, and Zod 4 does not catch exceptions thrown inside transforms, so:
Base64Encoded(z.any()).safeParse('not base64!!')throwsTypeError: Invalid base64 string.KinesisDataStreamSchema.safeParse(event)andKinesisEnvelope.safeParse(event, schema)throw the sameTypeError.parse(event, undefined, KinesisDataStreamSchema, true)throwsParseError: Schema parsing supports only synchronous validationand leaks an unhandled promise rejection. Zod's~standard.validatecatches the synchronous throw and falls back tosafeParseAsync, which returns a rejectingPromise.parse()sees aPromise, throws the misleading sync-only error, and never attaches a handler to the rejected promise. On the Lambda Node.js runtime this surfaces asRuntime.UnhandledPromiseRejection. The/* v8 ignore next */on that branch inparser.tsmarks it as unreachable, but it is reachable with the library's own built-in schema.- The Middy middleware with
safeParse: truethrows frombeforeinstead of handing the handler a failed result.
CloudWatchLogsSchema and KinesisFirehoseRecordSchema already guard with z.base64() before decoding and are not affected.
Code snippet
import { parse } from '@aws-lambda-powertools/parser';
import { Base64Encoded } from '@aws-lambda-powertools/parser/helpers';
import { KinesisDataStreamSchema } from '@aws-lambda-powertools/parser/schemas/kinesis';
import { z } from 'zod';
// 1. helper alone
Base64Encoded(z.any()).safeParse('not base64!!');
// throws TypeError: Invalid base64 string. (expected { success: false })
// 2. built-in schema through parse() with safeParse
const event = {
Records: [
{
eventSource: 'aws:kinesis',
eventVersion: '1.0',
eventID: 'shardId-000000000006:49590338271490256608559692538361571095921575989136588898',
eventName: 'aws:kinesis:record',
awsRegion: 'us-east-2',
invokeIdentityArn: 'arn:aws:iam::123456789012:role/lambda-role',
eventSourceARN: 'arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream',
kinesis: {
kinesisSchemaVersion: '1.0',
partitionKey: '1',
sequenceNumber: '49590338271490256608559692538361571095921575989136588898',
approximateArrivalTimestamp: 1545084650.987,
data: 'not base64!!',
},
},
],
};
parse(event, undefined, KinesisDataStreamSchema, true);
// throws ParseError: Schema parsing supports only synchronous validation
// and the process emits an unhandledRejection: TypeError: Invalid base64 string.Steps to Reproduce
- Create a Node.js 22.x or 24.x project with
@aws-lambda-powertools/parserandzod@4. - Run the snippet above with
node --unhandled-rejections=strict. - Observe the
TypeErrorescapingsafeParsein step 1, and theParseErrorplus a process-level unhandled rejection in step 2. - Wire the same event through
parser({ schema: KinesisDataStreamSchema, safeParse: true })as a Middy middleware and observebeforethrowing instead of settingrequest.eventto a failed result.
Possible Solution
- In
Base64Encoded, start the chain withz.base64()instead ofz.string()so invalid input becomes a normal Zod issue before the transform runs, matchingCloudWatchLogsSchemaandKinesisFirehoseRecordSchema. Alternatively wrap thefromBase64call and report viactx.addIssuewithfatal: true. - In
parse(), when~standard.validatereturns aPromise, attach a handler before throwing so a rejected promise from Zod's async fallback cannot leak. Dropping thev8 ignoreand covering the branch with a test would keep it honest.
Powertools for AWS Lambda (TypeScript) version
2.35.0 (main at 9bab0299)
AWS Lambda function runtime
22.x, 24.x
Packaging format used
npm
Execution logs
FAIL parse() with safeParse never throws when a Kinesis record carries malformed base64
ParseError: Schema parsing supports only synchronous validation
❯ parse src/parser.ts:124:11
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
TypeError: Invalid base64 string.
❯ fromBase64 ../commons/lib/esm/fromBase64.js:27:15
❯ Object.transform src/helpers/index.ts:108:38
❯ inst._zod.parse ../../node_modules/zod/v4/classic/schemas.js:1182:28
❯ handlePipeResult ../../node_modules/zod/v4/core/schemas.js:2174:22
Activity
vishwakt commentedon Sep 6, 2026
@svozza is this open for community contribution? If yes, i'd like to work on a fix
svozza commentedon Sep 7, 2026
Go for it!
powertools-for-aws-oss-automation commentedon Sep 7, 2026
Warning
This issue is now closed. Please be mindful that future comments are hard for our team to see.
If you need more assistance, please either reopen the issue, or open a new issue referencing this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.