Proxy integration error malformed serverless function response not a valid json

Yandex Cloud documentation. Contribute to yandex-cloud/docs development by creating an account on GitHub.

Вызов функции в {{ sf-name }}

Функцию можно вызвать:

  • с помощью HTTPS-запроса;
  • с помощью CLI;
  • с помощью триггера;
  • с помощью расширения {{ api-gw-full-name }}.

Для каждого способа есть определенная структура данных запроса к функции и ответа функции. Подробнее о том, как вызвать функцию.

Вызов функции с помощью HTTPS {#http}

Если функция вызывается для обработки HTTPS-запроса, то она получает первым аргументом данные о запросе в формате JSON: название HTTP-метода, заголовки, аргументы и другие параметры запроса.

Результат, который возвращает функция, также должен представлять из себя JSON-документ, содержащий код ответа HTTP, заголовки ответа и содержимое ответа. {{ sf-name }} автоматически обработает этот JSON, и пользователь получит данные в виде стандартного HTTPS-ответа.

{% note info %}

Вы можете запускать функцию с указанием параметра строки запроса integration=raw. При использовании такой формы вызова функция не может анализировать и задавать HTTP-заголовки:

  • Содержимое тела HTTPS-запроса передается первым аргументом (без преобразования в JSON-структуру).
  • Содержимое тела HTTPS-ответа совпадает с ответом функции (без преобразования и проверки структуры), HTTP-статус ответа: 200.

{% endnote %}

Структура запроса {#request}

JSON-структура запроса:

{
    "httpMethod": "<название HTTP-метода>",
    "headers": <словарь со строковыми значениями HTTP-заголовков>,
    "multiValueHeaders": <словарь со списками значений HTTP-заголовков>,
    "queryStringParameters": <словарь queryString-параметров>,
    "multiValueQueryStringParameters": <словарь списков значений queryString-параметров>,
    "requestContext": <словарь с контекстом запроса>,
    "body": "<содержимое запроса>",
    "isBase64Encoded": <true или false>
}

Где:

  • httpMethod — название HTTP-метода, такое как: DELETE, GET, HEAD, OPTIONS, PATCH, POST или PUT.

  • headers — словарь строк, содержащий HTTP-заголовки запроса и их значения. Если один и тот же заголовок передан несколько раз, словарь содержит последнее переданное значение.

  • multiValueHeaders — словарь, содержащий HTTP-заголовки запроса и списки с их значениями. Он содержит те же самые ключи, что и словарь headers, но если какой-либо заголовок повторялся несколько раз, список для него будет содержать все переданные значения для данного заголовка. Если заголовок был передан всего один раз, он включается в этот словарь, и список для него будет содержать одно значение.

  • queryStringParameters — словарь, содержащий параметры запроса. Если один и тот же параметр указан несколько раз, словарь содержит последнее указанное значение.

  • multiValueQueryStringParameters — словарь, содержащий для каждого параметра запроса список со всеми указанными значениями. Если один и тот же параметр указан несколько раз, словарь содержит все указанные значения.

  • requestContext содержит следующую структуру:

    {
        "identity": "<набор пар ключ:значение для аутентификации пользователя>",
        "httpMethod": "<DELETE, GET, HEAD, OPTIONS, PATCH, POST или PUT>",
        "requestId": "<ID запроса, генерируется в роутере>",
        "requestTime": "<время запроса в формате CLF>",
        "requestTimeEpoch": "<время запроса в формате Unix>",
        "authorizer": "<словарь с контекстом авторизации>",
        "apiGateway": "<словарь со специфичными данными, передаваемыми API-шлюзом при вызове функции>",
        "connectionId": "<идентификатор веб-сокетного соединения>",
        "connectedAt": "<время подключения веб-сокетного соединения>",
        "eventType": "<тип события или операции с веб-сокетом: CONNECT, MESSAGE, DISCONNECT>",
        "messageId": "<идентификатор сообщения, полученного из веб-сокета>",
        "disconnectStatusCode": "<статус-код закрытия веб-сокета>",
        "disconnectReason": "<текстовое описание причины закрытия веб-сокета>"
    }
    

    Структура элемента identity:

    {
        "sourceIp": "<адрес, с которого был сделан запрос>",
        "userAgent": "<содержимое HTTP-заголовка User-Agent исходного запроса>"
    }
    

    Структура элемента apiGateway:

    {
        "operationContext": "<словарь с контекстом операции, описанным в спецификации API-шлюза>"
    }
    
  • body — содержимое запроса в виде строки. Данные могут быть закодированы в формат Base64 (в этом случае {{ sf-name }} установит параметр isBase64Encoded: true).

    {% note info %}

    Если функция вызывается с заголовком Content-Type: application/json, то содержимое body останется в исходном формате (значение параметра isBase64Encoded: false).

    {% endnote %}

  • isBase64Encoded — если body содержит данные закодированные в Base64, то {{ sf-name }} установит значение параметра в true.

Отладка функции {#example}

Чтобы отладить обработку параметров, используйте функцию, которая возвращает JSON-структуру запроса. Пример такой функции приведен ниже.

module.exports.handler = async (event) => {
    return { 
        body: JSON.stringify(event)
    };
};

Например, для запроса:

curl -XPOST -d "hello, world!" "https://functions.yandexcloud.net/<ID функции>?a=1&a=2&b=1"

Результат будет выглядеть следующим образом:

{
  "httpMethod": "POST",
  "headers": {
    "Accept": "*/*",
    "Content-Length": "13",
    "Content-Type": "application/x-www-form-urlencoded",
    "User-Agent": "curl/7.58.0",
    "X-Real-Remote-Address": "[88.99.0.24]:37310",
    "X-Request-Id": "cd0d12cd-c5f1-4348-9dff-c50a78f1eb79",
    "X-Trace-Id": "92c5ad34-54f7-41df-a368-d4361bf376eb"
  },
  "path": "",
  "multiValueHeaders": {
    "Accept": [ "*/*" ],
    "Content-Length": [ "13" ],
    "Content-Type": [ "application/x-www-form-urlencoded" ],
    "User-Agent": [ "curl/7.58.0" ],
    "X-Real-Remote-Address": [ "[88.99.0.24]:37310" ],
    "X-Request-Id": [ "cd0d12cd-c5f1-4348-9dff-c50a78f1eb79" ],
    "X-Trace-Id": [ "92c5ad34-54f7-41df-a368-d4361bf376eb" ]
  },
  "queryStringParameters": {
    "a": "2",
    "b": "1"
  },
  "multiValueQueryStringParameters": {
    "a": [ "1", "2" ],
    "b": [ "1" ]
  },
  "requestContext": {
    "identity": {
      "sourceIp": "88.99.0.24",
      "userAgent": "curl/7.58.0"
    },
    "httpMethod": "POST",
    "requestId": "cd0d12cd-c5f1-4348-9dff-c50a78f1eb79",
    "requestTime": "26/Dec/2019:14:22:07 +0000",
    "requestTimeEpoch": 1577370127
  },
  "body": "aGVsbG8sIHdvcmxkIQ==",
  "isBase64Encoded": true
}

Служебные данные {#service-data}

Опционально функция может принимать второй аргумент со следующей структурой:

{
  "requestId": "<идентификатор запроса>",
  "functionName": "<идентификатор функции>",
  "functionVersion": "<идентификатор версии функции>",
  "memoryLimitInMB": "<объем памяти версии функции, МБ>",
  "token": "<опционально, IAM-токен>",
}

Где:

  • requestId — идентификатор запроса к функции, генерируется при обращении к функции и отображается в журнале вызова функции.
  • functionName — идентификатор функции.
  • functionVersion — идентификатор версии функции.
  • memoryLimitInMB — объем памяти, указанный для версии функции, МБ.
  • token — IAM-токен сервисного аккаунта, указанного для версии функции. Актуальное значение генерируется автоматически. Используется для работы с API {{ yandex-cloud }}. Поле присутствует, только если для версии функции указан корректный сервисный аккаунт.

Пример использования служебных данных в функции:

module.exports.handler = async (event, context) => {
    const iamToken = context.token;
    ...
    return { 
        body: ...
    };
};

Структура ответа {#response}

{{ sf-name }} интерпретирует результат выполнения функции для того, чтобы заполнить содержимое HTTPS-ответа, его заголовки и код состояния. Для этого функция должна возвращать ответ следующей структуры:

{
    "statusCode": <HTTP код ответа>,
    "headers": <словарь со строковыми значениями HTTP-заголовков>,
    "multiValueHeaders": <словарь со списками значений HTTP-заголовков>,
    "body": "<содержимое ответа>",
    "isBase64Encoded": <true или false>
}

Где:

  • statusCode — код состояния HTTP, по которому клиент узнаёт результаты запроса.
  • headers — словарь строк, содержащий HTTP-заголовки ответа и их значения.
  • multiValueHeaders — словарь, в котором для HTTP-заголовков ответа можно указать одно или несколько значений в виде списка. Если один и тот же заголовок указан и в headers, и в multiValueHeaders, содержимое headers игнорируется.
  • body — содержимое ответа в виде строки. Для работы с бинарными данными содержимое может быть закодировано в формат Base64. В этом случае установите параметр isBase64Encoded: true.
  • isBase64Encoded — если body закодирован в формат Base64, установите значение параметра в true.

Обработка ошибок в коде пользовательской функции {#error}

В случае возникновения необработанной ошибки в пользовательском коде, {{ sf-name }} вернет результат с кодом ошибки 502 и подробным описанием ошибки в виде следующей JSON-структуры:

{
  "errorMessage": "<сообщение ошибки>",
  "errorType": "<тип ошибки>",
  "stackTrace": <опционально, список вызываемых методов>
}

Где:

  • errorMessage — строка с описанием ошибки.
  • errorType — зависящий от языка программирования тип ошибки или исключения.
  • stackTrace — стек выполнения функции в момент возникновения ошибки.

Конкретное содержимое указанных полей зависит от языка программирования и среды выполнения вашей функции.

Ошибка в случае некорректной JSON-структуры ответа {#uncorrect-json}

Если структура ответа вашей функции не соответствует тому, что описано в разделе Структура данных ответа, то {{ sf-name }} вернет результат с кодом ошибки 502 и следующий ответ:

{
  "errorMessage": "Malformed serverless function response: not a valid json",
  "errorType": "ProxyIntegrationError",
  "payload": "<исходное содержимое ответа функции>"
}

Возможные коды ответа {{ sf-name }} {#http-state}

В случае ошибки в пользовательской функции к ответу добавляется заголовок X-Function-Error: true.

{{ sf-name }} может возвращать результат со следующими HTTP-кодами:

  • 200 OK — функция успешно выполнена.
  • 400 BadRequest — ошибка в параметрах HTTPS-запроса.
  • 403 Forbidden — запрос не может быть выполнен из-за ограничений в доступе для клиента к функции.
  • 404 Not Found — по указанному URL не найдена функция.
  • 413 Payload Too Large — превышение лимита на размер JSON-структуры запроса превышает 3,5 МБ.
  • 429 TooManyRequests — слишком высокая интенсивность вызова функции:
    • Превышение квоты на количество выполняемых запросов.
    • Текущий запрос не был выполнен, так как все исполнители уже перегружены существующими запросами к данной функции.
  • 500 Internal Server Error — внутренняя ошибка сервера.
  • 502 BadGateway — ошибка в коде функции или в формате возвращаемого JSON-ответа.
  • 503 Service Unavailable — недоступность сервиса {{ sf-name }}.
  • 504 Gateway Timeout — превышено максимальное время выполнения функции до таймаута.

Фильтрация заголовков сообщений {#headers}

Ваша функция получает и передает содержимое HTTP-заголовков в виде полей JSON-структуры, как описано выше. Некоторые заголовки запроса и ответа удаляются или переименовываются, как описано ниже.

{% list tabs %}

  • Заголовки запроса

    Удаляются из запроса:

    • «Expect»
    • «Te»
    • «Trailer»
    • «Upgrade»
    • «Proxy-Authenticate»
    • «Authorization»
    • «Connection»
    • «Content-Md5»
    • «Max-Forwards»
    • «Server»
    • «Transfer-Encoding»
    • «Www-Authenticate»
    • «Cookie»
  • Заголовки ответа

    • Удаляются из ответа:

      • «Host»
      • «Authorization»
      • «User-Agent»
      • «Connection»
      • «Max-Forwards»
      • «Cookie»
    • Вызывают ошибку, если присутствуют в ответе:

      • «Proxy-Authenticate»
      • «Transfer-Encoding»
      • «Via»
    • Перезаписываются с добавлением префикса X-Yf-Remapped-:

      • «Content-Md5»
      • «Date»
      • «Server»
      • «Www-Authenticate»

{% endlist %}

IP-адрес источника запроса {#ip}

Если в запросе есть заголовок X-Forwarded-For, в нем передаются указанные IP-адреса и IP-адрес клиента, который вызвал функцию. Иначе — только IP-адрес клиента, который вызвал функцию.

Вызов функции с помощью YC CLI {#cli}

Вызов функции с помощью CLI — это HTTPS-запрос с методом POST и параметром integration=raw (без преобразования запроса в JSON-структуру и без проверки ответа).

Посмотрите справку о команде вызова функции:

yc serverless function invoke --help
Invoke the specified function

Usage:
  yc serverless function invoke <FUNCTION-NAME>|<FUNCTION-ID> [Flags...] [Global Flags...]

Flags:
      --id string          Function id.
      --name string        Function name.
      --tag string         Tag. Default $latest.
  -d, --data string        Data to be sent
      --data-file string   Data (file location) to be sent
      --data-stdin         Await stdin for data to be sent

Подробное описание способов передачи данных с использованием разных флагов и аргументов:

  • Флаг или аргумент не указан — передается пустая строка.

  • -d, --data — данные передаются как аргумент.

    yc serverless function invoke <ID функции> -d '{"queryStringParameters": {"parameter_name": "parameter_value"}}'
    
  • --data-file — данные читаются из файла.

    yc serverless function invoke <ID функции> --data-file <путь к файлу>
    

    Аналогично команде с аргументом -d, имеющим значение @<имя файла>: yc serverless function invoke b09bhaokchn9pnbrlseb -d @<путь к файлу>

  • --data-stdin — данные читаются из потока ввода.

    echo '{"queryStringParameters": {"parameter_name": "parameter_value"}}' | yc serverless function invoke <ID функции> --data-stdin
    

    Аналогично команде с аргументом -d, имеющим значение @-:

    echo '{"queryStringParameters": {"parameter_name": "parameter_value"}}' | yc serverless function invoke <ID функции> -d @-`.
    

Вызов функции с помощью триггера {#trigger}

При вызове функции с помощью триггера JSON-описание события триггера передается в теле HTTP-запроса к функции. IP-адрес источника запроса передается так же, как и при вызове функции через HTTPS. Подробнее о триггерах.

Вызов функции с помощью расширения {{ api-gw-full-name }} {#extension}

При вызове функции с помощью расширения {{ api-gw-name }} функция получает HTTP-запрос, адресованный к API-шлюзу. В заголовке Host при этом указывается хост, по которому пользователь обратился к API-шлюзу, а не хост функции. IP-адрес источника запроса передается так же, как и при вызове функции через HTTPS. Подробнее о расширении в документации {{ api-gw-full-name }}.

How do I resolve HTTP 502 errors from API Gateway REST APIs with Lambda proxy integration?

Last updated: 2022-08-08

I configured Amazon API Gateway proxy integration to work with an AWS Lambda function. When I call my REST API, I receive a configuration error and an HTTP 502 status code. How do I resolve the issue?

Short description

If your Lambda function’s permissions are incorrect or the response to the API request isn’t formatted correctly, then API Gateway returns an HTTP 502 status code.

Example HTTP 502 error messages as it appears in Amazon CloudWatch Logs

Wed Aug 03 08:10:00 UTC 2022 : Execution failed due to configuration error: 
WE Aug 03 09:10:00 UTC 2022 : Method completed with status: 502

-or-

Wed Aug 03 08:20:33 UTC 2022 : Lambda execution failed with status 200 due to customer function error: [Errno 13] Permission denied: '/var/task/lambda_function.py'. Lambda request id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Wed Aug 03 08:20:33 UTC 2022 : Method completed with status: 502

For API Gateway to handle a Lambda function’s response, the function must return output according to the following JSON format:

{
    "isBase64Encoded": true|false,
    "statusCode": httpStatusCode,
    "headers": { "headerName": "headerValue", ... },
    "body": "..."
}

Resolution

exports.handler = async (event) => {

    const responseBody = {
        "key3": "value3",
        "key2": "value2",
        "key1": "value1"
    };

    const response = {
        "statusCode": 200,
        "headers": {
            "my_header": "my_value"
        },
        "body": JSON.stringify(responseBody),
        "isBase64Encoded": false
    };

    return response;
};

In this example response, there are four fields:

  • statusCode is an integer interpreted by API Gateway that’s returned to the caller of the API method.
  • headers are collected and then sent back with the API Gateway response.
  • body must be converted to a string if you’re returning data as JSON.
    Note: You can use JSON.stringify to handle this in Node.js functions. Other runtimes require different solutions, but the concept is the same.
  • isBase64Encoded is a required field if you’re working with binary data. If you don’t use this field, it’s a best practice to set the value to FALSE.

Did this article help?


Do you need billing or technical support?

AWS support for Internet Explorer ends on 07/31/2022. Supported browsers are Chrome, Firefox, Edge, and Safari.
Learn more »

Malformed Lambda proxy response error in AWS occurs when we configure Amazon API Gateway proxy integration to work with an AWS Lambda function.

When we call the API, it will return a configuration error or a 502 status code.

Here, at Bobcares, we assist our customers with several AWS queries as part of our AWS Support Services.

Today, let us see how our support techs resolve this.

Malformed Lambda proxy response error in AWS

Most often, the source of these errors is the format of the Lambda function’s response.

In such a case, we come across a message similar to this in the logs:

Thu Oct 08 01:13:00 UTC 2016 : Execution failed due to configuration error: Malformed Lambda proxy response
Thu Oct 08 01:13:00 UTC 2016 : Method completed with status: 502

For API Gateway to handle a Lambda function’s response, the response must be JSON in this format:

{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "..."
}

Moving ahead, let us see how our Support Techs resolve this.

First, we need to review the log output from the API Gateway console, or view the API Gateway log events in the Amazon CloudWatch console.

In the logs, we check the format of the Lambda function’s response to the API.

If it is not in JSON format, then we reformat it.

For example, here is a function in Node.js with the correct response format:

exports.handler = (event, context, callback) => {

var responseBody = {
"key3": "value3",
"key2": "value2",
"key1": "value1"
};

var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(responseBody),
"isBase64Encoded": false
};
callback(null, response);
};

In the result above, we can see four fields: statusCode, headers, body, and isBase64Encoded.

However, if the function is in another programming language, we need to refer to that language’s programming reference.

Then we have to confirm that the function returns a response in the correct format.

[Couldn’t find the resolution? We’d be happy to help you]

Conclusion

In short, we saw how our Support Techs fix the Malformed Lambda proxy response error in AWS

PREVENT YOUR SERVER FROM CRASHING!

Never again lose customers to poor server speed! Let us help you.

Our server experts will monitor & maintain your server 24/7 so that it remains lightning fast and secure.

GET STARTED

One problem that pops up quite frequently when people try to build serverless applications with AWS API Gateway and AWS Lambda is Execution failed due to configuration error: Malformed Lambda proxy response.

There is nothing worse than generic error messages that don’t tell you anything you need to fix the problem, right? And AWS isn’t particularly known for its error message design, if you can even call it that, let alone for giving you the means of fixing the problem. So how to fix this Lambda error and what causes it?

Fixing malformed Lambda proxy response

In order to fix this, you need to change what your Lambda function returns. And to do so, you need to return an object with two attributes:

  • statusCode – which is the HTTP status code you want to give your client with type number.
  • body – which is the content of your HTTP response with type string.

 If you used an asynchronous function, it should look like this:

exports.handler = async function(event, context) {
  return {statusCode: 200, body: "OK"};
};

If you’re more of a promise type of developer, it would look like this:

exports.handler = function(event, context) {
  return new Promise(function(resolove, reject) {
    resolve({statusCode: 200, body: "OK"});
  });
};

And finally, if you’re into callbacks, this will resolve your problem:

exports.handler = function(event, context, callback) {
  callback({statusCode: 200, body: "OK"});
};

Understanding the Malformed Lambda proxy error response

If you have some time to spare, why not learn a bit about the “Why” of the error along the way?

First, Malformed Lambda proxy response isn’t really a configuration error because the problem lies in your Lambda code. But still, it could very well be that the routine that checks your return value also checks for other things that can be configured from the outside, like from AWS CloudFormation.

But what about the proxy part of that error message? 

If you configure a route for your API in AWS API Gateway in the AWS Console, you get to choose an integration for that route, in our case, a Lambda integration. This integration is the glue between AWS Lambda and AWS API Gateway. After all, Lambda can be used for more than just handling API Gateway requests.

If you configure the route via CloudFormation, things get a bit clearer. 

ExampleApi:
  ...
DefaultStage:
  ...
ExampleRoute:
  Type: AWS::ApiGatewayV2::Route
  Properties:
    ApiId:
      Ref: ExampleApi
    RouteKey: ANY /
    Target:
      Fn::Join:
        - integrations/
        - Ref: ProxyIntegration
ProxyIntegration:
  Type: AWS::ApiGatewayV2::Integration
  Properties:
    ApiId:
      Ref: ExampleApi
    IntegrationType: AWS_PROXY
    IntegrationUri:
      Fn::GetAtt:
        - ExampleFunction
        - Arn
    PayloadFormatVersion: "2.0"
ExampleRole:
  ...
ExampleFunction:
  Type: AWS::Lambda::Function
  Properties:
    Code:
      ZipFile:
        'exports.handler = async () => ({ statusCode: 200, body: "Example" });'
    Handler: index.handler
    Role:
      Fn::GetAtt:
        - ExampleRole
        - Arn
    Runtime: nodejs12.x
  DependsOn:
    - ExampleRole

I guess you already spotted the important point here. The integration we create in the AWS Console is actually a specific type of integration, called AWS_PROXY. It’s used to integrate with many AWS services and the AWS Console just has a nice UI to make integrating with Lambda simpler.

There are actually five types of API Gateway integrations. Three you can use for your WebSockets endpoints and two for the HTTP endpoints.

HTTP Integration Types

When you build a regular HTTP API.

  1. AWS_PROXY to integrate with Lambda, or other AWS services, like Step Functions.
  2. HTTP_PROXY to pass-through a request to another HTTP endpoint.

WebSocket Integration Types

When you build a WebSocket based HTTP API.

  1. AWS to integrate with Lambda.
  2. HTTP to integrate with other (i.e., third-party) HTTP APIs.
  3. MOCK to make a WebSocket endpoint work as a loop-back, without any other service involved.

Summary

Error messages are a common pain point for developers, but often the fix is quite simple. However, if you know a bit of background to the error’s origin, it can help to fix it faster in the future.

To learn more about specific common serverless errors and how to fix them, make sure to visit our Event Library.

Read our blog

Introducing easy custom event monitoring for serverless applications.

Today we are excited to announce scheduled searches – a new feature on Dashbird that allows you to track any log event across your stack, turn it into time-series metric and also configure alert notifications based on it.

Why and How to Monitor Amazon OpenSearch Service

One of the most vital aspects to monitor is the metrics. You should know how your cluster performs and if it can keep up with the traffic. Learn more about monitoring Amazon OpenSearch Service.

Why and How to Monitor AWS Elastic Load Balancing

Dashbird recently added support for ELB, so now you can keep track of your load balancers in one central place. It comes with all the information you expect from AWS monitoring services and more!

Понравилась статья? Поделить с друзьями:
  • Proxy fcgi error primary script unknown
  • Provider named pipes provider error 40 не удалось открыть подключение к sql server
  • Proxy error the proxy server received an invalid response from an upstream server
  • Proxy error host is down i2p
  • Provider named pipes provider error 40 visual studio