Closed ArnauAregall closed 1 year ago
Adding a bit more info if it helps:
The error is also somehow reproducible when serializing the return type POJOs.
Managed to reproduce it by invoking the lambda using aws lambda invoke
cli command.
aws lambda invoke \
--function-name aws-lambda-spring-cloud-f-SpringCloudFunctionLambd-.... \
--cli-binary-format raw-in-base64-out \
--payload '[{"latitude": 42.99, "longitude": 2.83 }, {"latitude": 43.01, "longitude": 2.21 } ]' output.txt
Response:
{
"StatusCode": 200,
"FunctionError": "Unhandled",
"ExecutedVersion": "$LATEST"
}
output.txt
:
{
"errorType": "IllegalArgumentException",
"errorMessage": "Payload must not be null",
"stackTrace": "java.lang.IllegalArgumentException: Payload must not be null\n\tat org.springframework.util.Assert.notNull(Assert.java:204)\n\tat org.springframework.messaging.support.MessageBuilder.<init>(MessageBuilder.java:58)\n\tat org.springframework.messaging.support.MessageBuilder.withPayload(MessageBuilder.java:190)\n\tat org.springframework.cloud.function.adapter.aws.AWSLambdaUtils.generateOutputFromObject(AWSLambdaUtils.java:176)\n\tat org.springframework.cloud.function.adapter.aws.CustomRuntimeEventLoop.eventLoop(CustomRuntimeEventLoop.java:166)\n\tat org.springframework.cloud.function.adapter.aws.CustomRuntimeEventLoop.lambda$run$0(CustomRuntimeEventLoop.java:90)\n\tat java.base@17.0.5/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)\n\tat java.base@17.0.5/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)\n\tat java.base@17.0.5/java.lang.Thread.run(Thread.java:833)\n\tat org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:775)\n\tat org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:203)\n"
}
Logs:
2023-06-25T19:00:36.111Z DEBUG 9 --- [pool-5-thread-1] c.f.c.c.BeanFactoryAwareFunctionRegistry : Invoking function forecast<reactor.core.publisher.Flux<tech.aaregall.lab.function.weather.domain.GeoLocation>, reactor.core.publisher.Flux<tech.aaregall.lab.function.weather.domain.Forecast>>
2023-06-25T19:00:36.111000 2023-06-25T19:00:36.111Z DEBUG 9 --- [pool-5-thread-1] c.f.c.c.BeanFactoryAwareFunctionRegistry : Converting JSON string representing collection to a list of Messages. Function 'forecast<reactor.core.publisher.Flux<tech.aaregall.lab.function.weather.domain.GeoLocation>, reactor.core.publisher.Flux<tech.aaregall.lab.function.weather.domain.Forecast>>' will be invoked iteratively
2023-06-25T19:00:36.112000 2023-06-25T19:00:36.112Z DEBUG 9 --- [pool-5-thread-1] c.f.c.c.BeanFactoryAwareFunctionRegistry : Invoking function: forecast<reactor.core.publisher.Flux<tech.aaregall.lab.function.weather.domain.GeoLocation>, reactor.core.publisher.Flux<tech.aaregall.lab.function.weather.domain.Forecast>>with input type: reactor.core.publisher.Flux<tech.aaregall.lab.function.weather.domain.GeoLocation>
2023-06-25T19:00:36.112000 2023-06-25T19:00:36.112Z DEBUG 9 --- [pool-5-thread-1] o.s.c.f.a.aws.CustomRuntimeEventLoop : Reply from function: FluxMap
2023-06-25T19:00:36.112000 2023-06-25T19:00:36.112Z DEBUG 9 --- [pool-5-thread-1] c.f.c.c.BeanFactoryAwareFunctionRegistry : Converted Message: GenericMessage [payload={latitude=42.99, longitude=2.83}, headers={id=540951aa-c67b-22e2-56a7-6ed76137007a, timestamp=1687719636111}] to: class tech.aaregall.lab.function.weather.domain.GeoLocation
2023-06-25T19:00:36.112000 2023-06-25T19:00:36.112Z DEBUG 9 --- [pool-5-thread-1] c.f.c.c.BeanFactoryAwareFunctionRegistry : Converted Message: GenericMessage [payload={latitude=43.01, longitude=2.21}, headers={id=ab6b39c9-f56f-5d19-2512-22fbca5f7820, timestamp=1687719636111}] to: class tech.aaregall.lab.function.weather.domain.GeoLocation
2023-06-25T19:00:36.145000 2023-06-25T19:00:36.145Z DEBUG 9 --- [pool-5-thread-1] o.s.c.f.adapter.aws.AWSLambdaUtils : Response value: GenericMessage [payload=byte[11831], headers={contentType=application/json, id=7025cb06-5e20-7770-6884-90ca27b037ae, timestamp=1687719636145}]
2023-06-25T19:00:36.148000 2023-06-25T19:00:36.148Z DEBUG 9 --- [pool-5-thread-1] o.s.c.f.adapter.aws.AWSLambdaUtils : Response value: GenericMessage [payload=byte[11835], headers={contentType=application/json, id=0c1ca3d6-8c58-438e-d5a6-c0b8cdc8ddda, timestamp=1687719636148}]
2023-06-25T19:00:36.148000 2023-06-25T19:00:36.148Z DEBUG 9 --- [pool-5-thread-1] o.s.c.f.adapter.aws.AWSLambdaUtils : OUTPUT: [GenericMessage [payload=byte[11831], headers={contentType=application/json, id=7025cb06-5e20-7770-6884-90ca27b037ae, timestamp=1687719636145}], GenericMessage [payload=byte[11835], headers={contentType=application/json, id=0c1ca3d6-8c58-438e-d5a6-c0b8cdc8ddda, timestamp=1687719636148}]] - java.util.ArrayList
2023-06-25T19:00:36.151000 2023-06-25T19:00:36.150Z INFO 9 --- [pool-5-thread-1] o.s.c.f.a.aws.CustomRuntimeEventLoop : Result ERROR status: 202 ACCEPTED
Reading the logs it can be appreciated that the two JSON objects in the array of the payload were successfully converted to GeoLocation
POJOs.
But apparently it also failed on when converting the return type: org.springframework.cloud.function.adapter.aws.AWSLambdaUtils.generateOutputFromObject(AWSLambdaUtils.java:176)
OUTPUT ...
log produced by AWSLambdaUtils
also indicates the return class is an java.uti.ArrayList
whereas the function is defined with a return type of Flux<Forecast>
.
Was able to reproduce with your sample, so thank you. Will let you know once I figure out the issue
I thought it may have something to do with native hints, but just reproduced it locally, by sending a JSON representing an actual API Gateway request, so will have a test case for this.
That's actually really good news, thanks @olegz!
I believe I got it. I am assuming this is what you are expecting..?
curl -X POST -H "Content-Type: application/json" -d '[{"latitude": 41.34, "longitude": 2.78}]' https://emcdxu5ijj.execute-api.us-east-2.amazonaws.com/question
..
{"geoLocation":{"latitude":41.34,"longitude":2.7799997},"hourlyForecasts":[{"time":"2023-07-12T00:00:00","temperature":24.7,"precipitation":0.0},{"time":"2023-07-12T01:00:00","temperature":24.4,"precipitation":0.0},{"time":"2023-07-12T02:00:00","temperature":24.5,"precipitation":0.0},{"time":"2023-07-12T03:00:00","temperature":24.5,"precipitation":0.0},{"time":"2023-07-12T04:00:00","temperature":24.2,"precipitation":0.0},{"time":"2023-07-12T05:00:00","temperature":24.5,"precipitation":0.0},{"time":"2023-07-12T06:00:00","temperature":24.4,"precipitation":0.0},{"time":"2023-07-12T07:00:00","temperature":24.4,"precipitation":0.0},{"time":"2023-07-12T08:00:00","temperature":24.6,"precipitation":0.0},{"time":"2023-07-12T09:00:00","temperature":24.7,"precipitation":0.0},{"time":"2023-07-12T10:00:00","temperature":24.7,"precipitation":0.0},{"time":"2023-07-12T11:00:00","temperature":24.7,"precipitation":0.0},{"time":"2023-07-12T12:00:00","temperature":25.1,"precipitation":0.0},{"time":"2023-07-12T13:00:00","temperature":25.3,"precipitation":0.0},{"time":"2023-07-12T14:00:00","temperature":25.4,"precipitation":0.0},
Yes, that's it!
Although would be great to double check with multiple objects in the array, which was the original intended use case.
[
{
"latitude": 41.3874,
"longitude": 2.1686
},
{
"latitude": 47.9990,
"longitude": 7.8421
}
]
It's fixed, so give it a shot, I'll kick off a snapshot build so 4.0.5-SNAPSHOT will be available soon
Snapshots are now available
Tested OK on AWS with 4.0.5-SNAPSHOT, thanks a lot! 👍
Had to manually add org.springframework.cloud:spring-cloud-function-context
to confirm it (I was using it the one provided by s-c-f web).
implementation("org.springframework.cloud:spring-cloud-function-context:4.0.5-SNAPSHOT")
implementation("org.springframework.cloud:spring-cloud-function-web:4.0.5-SNAPSHOT")
implementation("org.springframework.cloud:spring-cloud-function-adapter-aws:4.0.5-SNAPSHOT")
Thanks @ArnauAregall will fix the sysout
with the next commit. Accidental leftover
I'm getting same issue.
I'm using spring cloud function version 4.1.0.
curl
command:
curl --header 'Content-Type: application/json' --data '{"label": "folder1", "references": ["028bba85-b605-4f25-be42-27e3d72566ec"]}' 'https://
.execute-api.eu-west-1.amazonaws.com:443/des/reference/4862ff49-c44e-47b0-99a2-34850ec45a20/linkage'
log I'm getting:
2024-04-05T10:18:48.494Z INFO 8 --- [ main] o.s.c.f.adapter.aws.FunctionInvoker : Located function: 'createReferenceLinkageFunction'
--
START RequestId: 9deaa3b1-6869-4a38-9b24-4e92891f0751 Version: $LATEST
2024-04-05T10:18:48.502Z INFO 8 --- [ main] o.s.c.f.adapter.aws.AWSLambdaUtils : Received: { "resource": "/reference/{referenceId}/linkage", "path": "/reference/4862ff49-c44e-47b0-99a2-34850ec45a20/linkage", "httpMethod": "POST", "headers": { "Accept": "*/*", "CloudFront-Forwarded-Proto": "https", "CloudFront-Is-Desktop-Viewer": "true", "CloudFront-Is-Mobile-Viewer": "false", "CloudFront-Is-SmartTV-Viewer": "false", "CloudFront-Is-Tablet-Viewer": "false", "CloudFront-Viewer-ASN": "35699", "CloudFront-Viewer-Country": "ES", "content-type": "application/json", "Host": "bad1udgbzk.execute-api.eu-west-1.amazonaws.com", "User-Agent": "curl/8.6.0", "Via": "2.0 3785ee12fd6da5a022c1747ed7b60a80.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "CeElKosFWSk4e6EcKG2fVKRJPkPcJvhq_j0FNm3PHNVIeCPd5RGMXw==", "X-Amzn-Trace-Id": "Root=1-660fcfff-599c465b4c6426b34c958f45", "X-Forwarded-For": "91.126.208.13, 15.158.57.19", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "multiValueHeaders": { "Accept": [ "*/*" ], "CloudFront-Forwarded-Proto": [ "https" ], "CloudFront-Is-Desktop-Viewer": [ "true" ], "CloudFront-Is-Mobile-Viewer": [ "false" ], "CloudFront-Is-SmartTV-Viewer": [ "false" ], "CloudFront-Is-Tablet-Viewer": [ "false" ], "CloudFront-Viewer-ASN": [ "35699" ], "CloudFront-Viewer-Country": [ "ES" ], "content-type": [ "application/json" ], "Host": [ "bad1udgbzk.execute-api.eu-west-1.amazonaws.com" ], "User-Agent": [ "curl/8.6.0" ], "Via": [ "2.0 3785ee12fd6da5a022c1747ed7b60a80.cloudfront.net (CloudFront)" ], "X-Amz-Cf-Id": [ "CeElKosFWSk4e6EcKG2fVKRJPkPcJvhq_j0FNm3PHNVIeCPd5RGMXw==" ], "X-Amzn-Trace-Id": [ "Root=1-660fcfff-599c465b4c6426b34c958f45" ], "X-Forwarded-For": [ "91.126.208.13, 15.158.57.19" ], "X-Forwarded-Port": [ "443" ], "X-Forwarded-Proto": [ "https" ] }, "queryStringParameters": null, "multiValueQueryStringParameters": null, "pathParameters": { "referenceId": "4862ff49-c44e-47b0-99a2-34850ec45a20" }, "stageVariables": null, "requestContext": { "resourceId": "jldtmb", "resourcePath": "/reference/{referenceId}/linkage", "httpMethod": "POST", "extendedRequestId": "Vv1v6EdiDoEEmzA=", "requestTime": "05/Apr/2024:10:18:39 +0000", "path": "/des/reference/4862ff49-c44e-47b0-99a2-34850ec45a20/linkage", "accountId": "058264555121", "protocol": "HTTP/1.1", "stage": "des", "domainPrefix": "bad1udgbzk", "requestTimeEpoch": 1712312319131, "requestId": "4aeb8a6f-d8bb-4ffc-add7-8d47dfecdced", "identity": { "cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "sourceIp": "91.126.208.13", "principalOrgId": null, "accessKey": null, "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "curl/8.6.0", "user": null }, "domainName": "bad1udgbzk.execute-api.eu-west-1.amazonaws.com", "deploymentId": "ru0w5s", "apiId": "bad1udgbzk" }, "body": "[{\"label\": \"folder1\", \"references\": [\"028bba85-b605-4f25-be42-27e3d72566ec\"]}]", "isBase64Encoded": false }
2024-04-05T10:18:48.996Z WARN 8 --- [ main] s.c.f.c.c.SmartCompositeMessageConverter : Failure during type conversion by org.springframework.cloud.function.adapter.aws.AWSTypesMessageConverter@43af351a. Will try the next converter.
java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class [B (java.util.LinkedHashMap and [B are in module java.base of loader 'bootstrap')
at org.springframework.cloud.function.adapter.aws.AWSTypesMessageConverter.convertFromInternal(AWSTypesMessageConverter.java:82) ~[task/:na]
at org.springframework.messaging.converter.AbstractMessageConverter.fromMessage(AbstractMessageConverter.java:183) ~[task/:na]
at org.springframework.messaging.converter.AbstractMessageConverter.fromMessage(AbstractMessageConverter.java:174) ~[task/:na]
at org.springframework.cloud.function.context.config.SmartCompositeMessageConverter.fromMessage(SmartCompositeMessageConverter.java:63) ~[task/:na]
at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.convertInputMessageIfNecessary(SimpleFunctionRegistry.java:1357) ~[task/:na]
at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.convertInputIfNecessary(SimpleFunctionRegistry.java:1120) ~[task/:na]
at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.lambda$convertInputPublisherIfNecessary$24(SimpleFunctionRegistry.java:1469) ~[task/:na]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113) ~[task/:na]
at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335) ~[task/:na]
at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294) ~[task/:na]
...
When I send request without body, it works.
Any ideas?
Describe the bug
Using:
3.1.1
2022.0.3
(SCF4.0.3
)Related to
org.springframework.cloud:spring-cloud-function-adapter-aws
:When running a Spring Cloud Function application on an AWS Lambda,
AWSTypesMessageConverter
seems to not be capable to correctly convert a JSON array body when the functions consume aFlux<T>
, whereT
is a regular application domain class/POJO.The domain class:
The function definition:
When invoking the AWS Lambda function with a body such as
It fails with the following Jackson conversion error:
Nevertheless it works perfectly when the application is not running on AWS (locally, or on a Docker Image) being able to take more "geolocations" in the payload, which is the intention.
Sample
I built a sample application to demonstrate the issue: https://github.com/ArnauAregall/aws-lambda-spring-cloud-function-reactive-java
If you clone and run the application locally:
The
response.json
file will be a JSON array containing a single object representing the weather forecast for those coordinates.But when running in AWS:
The same command against the lambda URL will result in "Internal Server Error" with the mentioned stacktrace above.