Fastapi: h11._util.LocalProtocolError: Too much data for declared Content-Length - when returning dict as response, with custom status code

Created on 27 Oct 2020  路  3Comments  路  Source: tiangolo/fastapi

First check

  • [x] I added a very descriptive title to this issue.
  • [x] I used the GitHub search to find a similar issue and didn't find it.
  • [x] I searched the FastAPI documentation, with the integrated search.
  • [x] I already searched in Google "How to X in FastAPI" and didn't find any information.
  • [x] I already read and followed all the tutorial in the docs and didn't find an answer.
  • [x] I already checked if it is not related to FastAPI but to Pydantic.
  • [x] I already checked if it is not related to FastAPI but to Swagger UI.
  • [x] I already checked if it is not related to FastAPI but to ReDoc.
  • [x] After submitting this, I commit to one of:

    • Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.

    • I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.

    • Implement a Pull Request for a confirmed bug.

Example

Here's a self-contained, minimal, reproducible, example with my use case:

from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
import uvicorn

app = FastAPI()

class MyModel(BaseModel):
    foo: str
    bar: int

RESPONSE = {"message": "Hello there!"}

@app.get("/get1")
def get1():
    return RESPONSE

@app.get("/get2", status_code=204)
def get2():
    return RESPONSE

@app.post("/post1")
def post1():
    return RESPONSE

@app.post("/post2")
def post2(data: MyModel):
    return RESPONSE

@app.post("/post3", status_code=204)
def post3():
    return RESPONSE

@app.post("/post4", status_code=204)
def post4(data: MyModel):
    return RESPONSE

@app.post("/post5")
def post5(data: MyModel, status_code=204):
    return JSONResponse(
        content=RESPONSE,
        status_code=204 # must define status_code here
    )

if __name__ == "__main__":
    uvicorn.run(app, port=5000)

Description

On the given code, the endpoints /get2, /post3 and /post4 fail, raising this exception:

INFO:     127.0.0.1:46692 - "POST /post4 HTTP/1.1" 204 No Content
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 389, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/fastapi/applications.py", line 179, in __call__
    await super().__call__(scope, receive, send)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/applications.py", line 111, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/routing.py", line 566, in __call__
    await route.handle(scope, receive, send)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/routing.py", line 227, in handle
    await self.app(scope, receive, send)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/routing.py", line 44, in app
    await response(scope, receive, send)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/responses.py", line 139, in __call__
    await send({"type": "http.response.body", "body": self.body})
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/exceptions.py", line 68, in sender
    await send(message)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/starlette/middleware/errors.py", line 156, in _send
    await send(message)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 483, in send
    output = self.conn.send(event)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/h11/_connection.py", line 469, in send
    data_list = self.send_with_data_passthrough(event)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/h11/_connection.py", line 502, in send_with_data_passthrough
    writer(event, data_list.append)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/h11/_writers.py", line 78, in __call__
    self.send_data(event.data, write)
  File "/home/david/.miniconda3/envs/fastapi+pydantic/lib/python3.8/site-packages/h11/_writers.py", line 98, in send_data
    raise LocalProtocolError("Too much data for declared Content-Length")
h11._util.LocalProtocolError: Too much data for declared Content-Length

It seems that this fails when returning a dict as response (without using JSONResponse), AND setting a status_code for the endpoint.

Environment

  • OS: Ubuntu 18.04
  • Python version: 3.8.5
  • FastAPI version: 0.61.1
  • Uvicorn version: 0.12.2
question

Most helpful comment

To make valid endpoint that returns 204 status code you should use Response object:

@app.post("/post5")
def post5(data: MyModel, status_code=HTTPStatus.NO_CONTENT):
    return Response(status_code=HTTPStatus.NO_CONTENT.value)

All 3 comments

HTTP 204 No Content cannot contain a message body: https://tools.ietf.org/html/rfc7230#section-3.3.3
"Any response to a HEAD request and any response with a 1xx (Informational), 204 (No Content), or 304 (Not Modified) status code is always terminated by the first empty line after the header fields, regardless of the header fields present in the message, and thus cannot contain a message body."

content-length forced in h11 to be equal to 0 (h11._connection.py:75) so when you return something in this endpoints there is exception raised

To make valid endpoint that returns 204 status code you should use Response object:

@app.post("/post5")
def post5(data: MyModel, status_code=HTTPStatus.NO_CONTENT):
    return Response(status_code=HTTPStatus.NO_CONTENT.value)

@SirTelemak Thanks for the clarification! Big facepalm for me haha :facepalm:

Was this page helpful?
0 / 5 - 0 ratings