Closed JUSTINMKAUFMAN closed 1 week ago
is your CDK removing the AWS SDK from your application bundle?
If so, you should turn that option off. This operation may have not made it into the AWS Lambda provided version of the AWS SDK for JavaScript (v3) yet.
https://docs.aws.amazon.com/lambda/latest/operatorguide/sdks-functions.html beyond simple testing you should configure the CDK or any other tool to use your own pinned version of the AWS SDK.
You should be able check what exports are available like this:
import * as namespace from "@aws-sdk/client-pinpoint-sms-voice-v2";
console.log(Object.keys(namespace));
Or, using CJS to check the version number:
console.log(
require("@aws-sdk/client-pinpoint-sms-voice-v2/package.json").version
);
Thank you very much for the quick reply!
In my cdk.json
I have (in relevant part) { "watch": { "exclude": ["node_modules"] } }
, is that the option you're referring to?
In my "context" object (in the same file) I have:
"@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
"@aws-cdk/core:stackRelativeExports": true,
"@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
"@aws-cdk/aws-lambda:recognizeVersionProps": true,
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true,
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
]
...not sure any of those look right though.
Finally, the code to create the NodeJsFunction has the following props:
{
functionName: 'sendMMS',
runtime: Runtime.NODEJS_18_X,
handler: 'handler',
entry: `functions/sendMMSHandler.ts`,
architecture: Architecture.ARM_64,
timeout: Duration.minutes(15),
awsSdkConnectionReuse: true,
bundling: {
minify: false,
sourceMap: true,
target: 'es2020'
},
environment: {
'NODE_OPTIONS': '--enable-source-maps'
}
}
If you could point me to the option you are referencing (that would cause CDK to remove the AWS SDK from my application bundle), that'd be much appreciated. Thanks again.
I took a shot at adding the library to the 'externalModules' array in my NodeJsFunction bundling props, and now I get this error:
TypeError: import_client_pinpoint_sms_voice_v2.SendMediaMessageCommand is not a constructor
(similar).
Hi @JUSTINMKAUFMAN ,
Just to add to what @kuhe said, this issue is likely happening because when you deploy your Lambda using CDK, the Lambda function comes pre-installed with the lambda provided SDK version that is not @latest
. Looking at the SendMediaMessageCommand
API command, it was added 2 months ago, and Lambda only updates their provided SDK version 1-2 times a year which means this operation doesn't exist on the SDK version running from your lambda function.
In normal SDK-land, fixing this would mean bundling and minifying your application and deploying to lambda, or providing the desired SDK version as a Lambda Layer. In CDK-land you'll likely need to do CDK specific things to get it to work. Ideally CDK questions would go to the CDK repo since that team deals with CDK specific issues and will be better equipped to assist you.
That being said, I found this issue on their repo that seems to be directly related to the issue you are having. The suggestion is to specify:
bundling: {
externalModules: [],
// more options,
}
Can you give the workaround there a try and let us know if it solves the issue?
Thanks, Ran~
Thanks for the follow-up @RanVaknin. I did manage to solve this after trying a number of different things. My learnings:
I wasn't able to resolve this simply by changing the configuration of the lambda construct itself (including, but not limited to, your suggestion around the externalModules
property).
My understanding is there are 2 ways to tackle this in a CDK app.
@aws-sdk/*
as an external module, and deploy the lambda as a dockerized image. I did not wind up doing this.This took a bunch of tinkering to get working (I couldn't find any specific documentation on how to build/consume a layer that simply exposes the aws-sdk
modules you need), so here is what I wound up with in case it helps someone else:
The stack for the lambda layer:
import { Construct } from 'constructs'
import { Stack, StackProps } from 'aws-cdk-lib'
import { StringParameter } from 'aws-cdk-lib/aws-ssm'
import { Architecture, Code, LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'
export class LambdaStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props)
const awsSdkLayer = new LayerVersion(this, 'AwsSdkLayer', {
layerVersionName: 'AwsSdkLayer',
code: Code.fromAsset('lambda/layers/awsSdkLayer'),
compatibleRuntimes: [Runtime.NODEJS_18_X],
compatibleArchitectures: [Architecture.ARM_64]
})
new StringParameter(this, 'aws-sdk-layer-arn', { parameterName: `/Lambda/awsSdkLayerArn`, stringValue: awkSdkLayer.layerVersionArn })
}
}
Then I created a folder structure like:
./lambda/layers/awsSdkLayer/nodejs
...and inside nodejs
, I created 2 files:
package.json
:
{
"name": "aws-sdk-layer",
"version": "0.1.0",
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.596.0",
"@aws-sdk/client-cognito-identity-provider": "^3.596.0",
"@aws-sdk/client-dynamodb": "^3.596.0",
"@aws-sdk/client-lightsail": "^3.596.0",
"@aws-sdk/client-pinpoint-sms-voice-v2": "^3.596.0",
"@aws-sdk/client-s3": "^3.596.0",
"@aws-sdk/client-secrets-manager": "^3.596.0",
"@aws-sdk/client-ses": "^3.596.0",
"@aws-sdk/client-sesv2": "^3.596.0",
"@aws-sdk/client-sns": "^3.596.0",
"@aws-sdk/client-sqs": "^3.596.0",
"@aws-sdk/lib-dynamodb": "^3.596.0",
"@aws-sdk/s3-request-presigner": "^3.596.0",
"@aws-sdk/util-dynamodb": "^3.596.0"
}
}
tsconfig.json
{ "extends": "../../../../tsconfig.json" }
(Given that I ultimately stripped everything out of it, I probably didn't need the tsconfig after all).
And in the stack where the lambda gets created:
// Getting the layer via SSM
const parameter = StringParameter.fromStringParameterName(scope, `aws-sdk-layer-arn`, '/Lambda/awsSdkLayerArn')
const awsSdkLayer = LayerVersion.fromLayerVersionArn(scope, `aws-sdk-layer`, parameter.stringValue)
// Creating the lambda
new NodejsFunction(scope, `myLambda`, {
functionName: 'myLambda',
runtime: Runtime.NODEJS_18_X,
handler: 'handler',
entry: `functions/myLambda.ts`,
architecture: Architecture.ARM_64,
timeout: Duration.minutes(15),
layers: [awsSdkLayer], // ---> IMPORT THE LAYER
bundling: {
minify: false,
sourceMap: true,
target: 'es2020',
externalModules: ['@aws-sdk/client-pinpoint-sms-voice-v2'] // ---> DECLARE EXTERNAL MODULES
},
environment: {
'NODE_OPTIONS': '--enable-source-maps'
}
})
I am confident this could be further optimized, or even generally approached in a more sophisticated way (and I'm all ears if you have suggestions!), but I was just relieved to finally see the runtime errors disappear.
Great to hear that you worked through this.
I don't have any suggestions since Im not super familiar with CDK, but like you said there are multiple ways to go about this. Im going to close this issue but your answer will remain discoverable.
Thanks again, Ran~
Checkboxes for prior research
Describe the bug
The error in the title (
TypeError: SendMediaMessageCommand is not a constructor
) is raised when trying to send an MMS message using the@aws-sdk/client-pinpoint-sms-voice-v2
dependency.I am following the example in the
SendMediaMessageCommand
documentation to the letter.Similar code in my stack for using
@aws-sdk/client-sns
to send a regular SMS works just fine (e.g.const command = new PublishCommand({ PhoneNumber: phone, Message: body })
).SDK version number
@aws-sdk/client-pinpoint-sms-voice-v2@3.596.0
Which JavaScript Runtime is this issue in?
Node.js
Details of the browser/Node.js/ReactNative version
v18.15.0
Reproduction Steps
Observed Behavior
TypeError: SendMediaMessageCommand is not a constructor
Expected Behavior
The code to use the documented constructor for SendMediaMessageCommand with no errors.
Possible Solution
I don't know what is causing this issue so it's hard to suggest a solution, but I'd start by comparing the source code for instantiating other Command classes (such as the @aws-sdk/client-sns
PublishCommand
, which works fine for me) to the SendMediaMessageCommand to find potential discrepancies.Additional Information/Context
This code comes from my AWS CDK app which is written in Typescript.
Here is my
tsconfig.json
:...and here is my
package.json
(in relevant part):