.NET Lambda on LocalStack throws "exception while calling lambda with unknown operation:"

Hi,
I wanted to test a simple lambda function written in .NET on my localstack.
I’m using a docker compose file to run Localstack on my local windows machine.

version: "3.8"

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}"
    image: localstack/localstack
    ports:
      #- "127.0.0.1:4566:4566"            # LocalStack Gateway
      #- "127.0.0.1:4510-4559:4510-4559"  # external services port range
      - 4566:4566
    environment:
      # LocalStack configuration: https://docs.localstack.cloud/references/configuration/
      - SERVICES=s3,sql,lambda
      - LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT=50
      - AWS_DEFAULT_REGION=us-east-1
      - AWS_ENDPOINT= http://localstack:4566
      - DEBUG=${DEBUG:-1}
    volumes:
      - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"

The .net code is as follows. (This is a very simple minimal API)

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAWSLambdaHosting(LambdaEventSource.HttpApi);
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapGet("hello/", () => $"Hello");

app.Run();

I create the .zip file using folowing command

dotnet lambda package --configuration release --framework net8.0 --output-package releases/hellolambda.zip

and deployed to localstack using the following command

awslocal lambda create-function --function-name hellolambda --runtime dotnet8 --timeout 50 --zip-file fileb://releases/hellolambda.zip --handler hellolambda.handler --role arn:aws:iam::000000000000:role/lambda-role

so in order get access to the endpoint of my function, I create a lambda function url as stated in the following blog

 awslocal lambda create-function-url-config --function-name hellolambda --auth-type NONE

It resulted the following:

 {
    "FunctionUrl": "http://hq2cbj7kw7ag6zp8pidtvct0jwr7zyab.lambda-url.us-east-1.localhost.localstack.cloud:4566/",
    "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:hellolambda",
    "AuthType": "NONE",
    "CreationTime": "2024-09-12T12:05:25.719615+0000"
}

When I try to access the above url from my browser. I got an error.

The error in plain text as spitted in the console

 Traceback (most recent call last):
localstack-main  |   File "/opt/code/localstack/localstack-core/localstack/services/lambda_/invocation/executor_endpoint.py", line 166, in wait_for_startup
localstack-main  |     self.startup_future.result()
localstack-main  |   File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 454, in result
localstack-main  |     raise CancelledError()
localstack-main  | concurrent.futures._base.CancelledError
localstack-main  |
localstack-main  | The above exception was the direct cause of the following exception:
localstack-main  |
localstack-main  | Traceback (most recent call last):
localstack-main  |   File "/opt/code/localstack/localstack-core/localstack/services/lambda_/invocation/execution_environment.py", line 198, in start
localstack-main  |     self.runtime_executor.start(self.get_environment_variables())
localstack-main  |   File "/opt/code/localstack/localstack-core/localstack/services/lambda_/invocation/docker_runtime_executor.py", line 430, in start
localstack-main  |     self.executor_endpoint.wait_for_startup()
localstack-main  |   File "/opt/code/localstack/localstack-core/localstack/services/lambda_/invocation/executor_endpoint.py", line 170, in wait_for_startup
localstack-main  |     raise ShutdownDuringStartup(
localstack-main  | localstack.services.lambda_.invocation.executor_endpoint.ShutdownDuringStartup: Executor environment shutdown during container startup
localstack-main  |
localstack-main  | The above exception was the direct cause of the following exception:
localstack-main  |
localstack-main  | Traceback (most recent call last):
localstack-main  |   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/rolo/gateway/chain.py", line 166, in handle
localstack-main  |     handler(self, self.context, response)
localstack-main  |   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/rolo/gateway/handlers.py", line 27, in __call__
localstack-main  |     router_response = self.router.dispatch(context.request)
localstack-main  |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
localstack-main  |   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/rolo/router.py", line 381, in dispatch
localstack-main  |     return self.dispatcher(request, handler, args)
localstack-main  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
localstack-main  |   File "/opt/code/localstack/.venv/lib/python3.11/site-packages/rolo/dispatcher.py", line 72, in _dispatch
localstack-main  |     result = endpoint(request, **args)
localstack-main  |              ^^^^^^^^^^^^^^^^^^^^^^^^^
localstack-main  |   File "/opt/code/localstack/localstack-core/localstack/services/lambda_/urlrouter.py", line 87, in handle_lambda_url_invocation
localstack-main  |     result = self.lambda_service.invoke(
localstack-main  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
localstack-main  |   File "/opt/code/localstack/localstack-core/localstack/services/lambda_/invocation/lambda_service.py", line 329, in invoke
localstack-main  |     return version_manager.invoke(
localstack-main  |            ^^^^^^^^^^^^^^^^^^^^^^^
localstack-main  |   File "/opt/code/localstack/localstack-core/localstack/services/lambda_/invocation/version_manager.py", line 196, in invoke
localstack-main  |     with self.assignment_service.get_environment(
localstack-main  |   File "/usr/local/lib/python3.11/contextlib.py", line 137, in __enter__
localstack-main  |     return next(self.gen)
localstack-main  |            ^^^^^^^^^^^^^^
localstack-main  |   File "/opt/code/localstack/localstack-core/localstack/services/lambda_/invocation/assignment.py", line 68, in get_environment
localstack-main  |     execution_environment = self.start_environment(version_manager_id, function_version)
localstack-main  |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
localstack-main  |   File "/opt/code/localstack/localstack-core/localstack/services/lambda_/invocation/assignment.py", line 97, in start_environment
localstack-main  |     execution_environment.start()
localstack-main  |   File "/opt/code/localstack/localstack-core/localstack/services/lambda_/invocation/execution_environment.py", line 212, in start
localstack-main  |     raise EnvironmentStartupTimeoutException(
localstack-main  | localstack.services.lambda_.invocation.execution_environment.EnvironmentStartupTimeoutException: Execution environment timed out during startup.
localstack-main  | 2024-09-12T12:08:59.108  INFO --- [et.reactor-4] localstack.request.http    : GET /hello/ => 500

Also note that, if I run the application on my localhost as a normal web-api deployment, I can access the endpoint and see the results on my browser.

Can someone help me to understand what is wrong with my deployment?
am I missing any configurations or am I not doing the things in correct order?
appreciate your help in this regard.

Regards

Hi,

Here is a sample that I used to deploy the dotnet Lambda.
Please also have a look at localstack-dotnet/localstack-dotnet-client: A lightweight .NET client for LocalStack (github.com).

# https://docs.aws.amazon.com/lambda/latest/dg/csharp-package-cli.html
# https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/lambda-cli-publish.html
# https://learn.microsoft.com/en-us/dotnet/core/rid-catalog
# https://stackoverflow.com/questions/66551987/how-to-run-aws-lambda-dotnet-on-localstack
# https://docs.localstack.cloud/user-guide/integrations/aws-cli/

# Install Lambda templates and tools
dotnet new install Amazon.Lambda.Templates
dotnet tool install -g Amazon.Lambda.Tools

# Create a new Lambda project
dotnet new lambda.EmptyFunction --name myDotnetFunction

# Change to the project directory
Set-Location myDotnetFunction\src\myDotnetFunction

# Restore the project dependencies
dotnet restore

# Build the project
dotnet build
dotnet publish -c Release -o publish

# Change to the publish directory
Set-Location publish

# Create a zip file of the project
Compress-Archive -Path * -DestinationPath myDotnetFunction.zip

awslocal lambda create-function `
    --function-name myDotnetFunction `
    --runtime dotnet8 `
    --role arn:aws:iam::000000000000:role/lambda-role `
    --handler myDotnetFunction::myDotnetFunction.Function::FunctionHandler `
    --zip-file fileb://myDotnetFunction.zip `
    --region us-east-1

# Invoke the Lambda function
awslocal lambda invoke --function-name myDotnetFunction --payload '"Hello, world!"' output.json

Adding a bit of different approach:

# Prerequisites:
# 1. .NET SDK installed (https://dotnet.microsoft.com/download)
# 2. AWS CLI installed and configured (https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)
# 3. LocalStack installed and running (https://docs.localstack.cloud/getting-started/)
# 4. Amazon.Lambda.Tools installed (dotnet tool install -g Amazon.Lambda.Tools)
# 5. Amazon.Lambda.Templates installed (dotnet new install Amazon.Lambda.Templates)

# https://docs.aws.amazon.com/lambda/latest/dg/csharp-package-cli.html
# https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/lambda-cli-publish.html
# https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-endpoints.html

# Install Lambda templates and tools
dotnet new install Amazon.Lambda.Templates
dotnet tool install -g Amazon.Lambda.Tools

# Create a new Lambda project
dotnet new lambda.EmptyFunction --name myDotnetFunction

# Change to the project directory
Push-Location -Path "myDotnetFunction/src/myDotnetFunction"

# Set environment variables for LocalStack
$env:AWS_ENDPOINT_URL = "http://localhost.localstack.cloud:4566"
$env:AWS_ACCESS_KEY_ID = "test"
$env:AWS_SECRET_ACCESS_KEY = "test"
$env:AWS_DEFAULT_REGION = "us-east-1"

# Deploy the Lambda function
dotnet lambda deploy-function `
    --use-container-for-build $true `
    --function-role "arn:aws:iam::000000000000:role/lambda-role" `
    --function-name "myDotnetFunction" `
    --framework "net8.0" `
    --function-architecture "x86_64" `
    --environment-variables "TEST_VAR=TEST_VAL1"

# Invoke the Lambda function
dotnet lambda invoke-function "myDotnetFunction" --payload '"Hello, world!"'

# Return to the previous directory
Pop-Location

Thank you very much for the links and the steps. I’ll try this and share the outcome.