nginx / unit

NGINX Unit - universal web app server - a lightweight and versatile open source server that simplifies the application stack by natively executing application code across eight different programming language runtimes.
https://unit.nginx.org
Apache License 2.0
5.25k stars 321 forks source link

[error] 75#75 [unit] #10: the application returned not an iterable object #1236

Closed BwL1289 closed 1 month ago

BwL1289 commented 2 months ago

Hello,

Sorry in advance if this is a dumb question.

We just moved our monolithic Flask App from Lambda to Ecs Fargate w/ Nginx Unit.

Everything seems to be working except I am receiving the following error after the app starts, apis are registered, and the api call is processed successfully by the application:

2024/04/28 16:20:17 [error] 75#75 [unit] #10: the application returned not an iterable object
TypeError: 'NoneType' object is not iterable

This error is raised after we return a Flask Response object (<Response 1548 bytes [200 OK]>) or json.dumps(...) (the exit point of the app).

I see on this line the error is raised, but it's unclear to me how to mitigate this.

More context: for now, we're using wsgi protocol, and we proxy api calls to the relevant api and ultimately wrap Flask's Response object in a custom Success and Error classes as exit points.

How can I further debug this?

BwL1289 commented 2 months ago

The Response object in Flask is not iterable, so the C call to PyObject_GetIter returns NULL and raises this error.

EDIT: the above still occurs even if I return json.dumps(...). Unclear why response cannot be turned into an iterator. Changed the above to reflect that.

ac000 commented 2 months ago

Hi,

Is this an ASGI or WSGI application?

Do you have a simple reproducer? Doesn't even need to be an actual flask app, just something which triggers the error...

BwL1289 commented 2 months ago

Closing this as it was user error. To anyone who finds this, make sure you are returning something from your entrypoint handler, and make sure you are serializing that return statement.

ac000 commented 2 months ago

Hi,

I created a python file gh1236.py as

import json;

class JSONEncoder(json.JSONEncoder):
    def default(self, obj):
        try:
            if isinstance(obj, ObjectId):
                return str(obj)
            if isinstance(obj, datetime):
                return str(obj)
            return json.JSONEncoder.default(self, obj)
        except Exception as err:
            logger.exception(f"There was an error: {err}")

class Response:
    @staticmethod
    def json(
        response: dict[str, Any],
        status_code: int,
        mimetype: str = "application/json",
    ) -> Any | None:
        json_resp = Response(
            response=json.dumps(response, cls=JSONEncoder),
            status=status_code,
            mimetype=mimetype,
        )

        logger.info(f"json: {json_resp.json}")

        return json_resp

class SuccessResponse(Response):
    def __new__(cls, payload: dict[str, Any] | None = None, status_code: int = 200) -> Response:
        response: dict[str, Any] = {"status": "success", "result": payload}
        return cls.json(response, status_code)

def handler(unit_environ: dict[str, Any], unit_start_response) -> None:
    event_json = {}
    unit_start_response("200 OK", [("Content-Type", "application/json; charset=utf-8")])  # needed?
    lambda_handler(unit_environ, {})

def get(self):
    try:
        found_organizations = {}
        num_org_records = len(found_organizations)
    except Exception as error:
        logger.exception(error)
        return ErrorResponse("Failed", error.args)
    else:
        return SuccessResponse({"records": found_organizations})

and a gh1236.json as

{
    "listeners": {
        "[::1]:8080": {
            "pass": "applications/python"
        }
    },

    "applications": {
        "python": {
            "type": "python",
            "path": "/home/andrew/src/python",
            "module": "gh1236",
            "callable": "handler"
        }
    }
}

I had to import json but am still getting this error when trying to load it

2024/04/29 20:25:35 [alert] 4188#4188 Python failed to import module "gh1236"
Traceback (most recent call last):
  File "/home/andrew/src/python/gh1236.py", line 14, in <module>
    class Response:
  File "/home/andrew/src/python/gh1236.py", line 17, in Response
    response: dict[str, Any],
                        ^^^
NameError: name 'Any' is not defined. Did you mean: 'any'?