question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Incorrect requests to websockets raise validation error exception

See original GitHub issue

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn’t find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google “How to X in FastAPI” and didn’t find any information.
  • I already read and followed all the tutorial in the docs and didn’t find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

from fastapi import FastAPI, WebSocket
from fastapi.websockets import WebSocketDisconnect


app = FastAPI()


@app.websocket('/test')
async def test(websocket: WebSocket, prefix: str):
	await websocket.accept()
	try:
		while True:
			message = await websocket.receive_text()
			await websocket.send_text(prefix+message)
	except WebSocketDisconnect:
		pass

Description

Hello, When i connect to websocket with prefix query parameter all works fine. But when i connect without prefix parameter, a few bad things happen.

Console 1

D:\temp>uvicorn --no-use-colors ws_test:app
INFO:     Started server process [20404]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Console 2

C:\Users\Danstiv>python -m websockets ws://localhost:8000/test
Failed to connect to ws://localhost:8000/test: server rejected WebSocket connection: HTTP 403.

Console 1

INFO:     ('127.0.0.1', 65175) - "WebSocket /test" 403
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "c:\python39\lib\site-packages\uvicorn\protocols\websockets\websockets_impl.py", line 171, in run_asgi
    result = await self.app(self.scope, self.asgi_receive, self.asgi_send)
  File "c:\python39\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 59, in __call__
    return await self.app(scope, receive, send)
  File "c:\python39\lib\site-packages\fastapi\applications.py", line 208, in __call__
    await super().__call__(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\applications.py", line 112, in __call__
    await self.middleware_stack(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\middleware\errors.py", line 146, in __call__
    await self.app(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\exceptions.py", line 58, in __call__
    await self.app(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\routing.py", line 580, in __call__
    await route.handle(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\routing.py", line 68, in app
    await func(session)
  File "c:\python39\lib\site-packages\fastapi\routing.py", line 264, in app
    raise WebSocketRequestValidationError(errors)
fastapi.exceptions.WebSocketRequestValidationError: 1 validation error for WebSocket
query -> prefix
  field required (type=value_error.missing)

Intercepted traffic

GET /test HTTP/1.1
Host: localhost:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: LKV8aWdjooqq25dxMBhDNQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
User-Agent: Python/3.9 websockets/9.1


HTTP/1.1 403 Forbidden
Date: Sat, 14 Aug 2021 11:09:36 GMT
Server: Python/3.9 websockets/9.1
Content-Length: 0
Content-Type: text/plain
Connection: close

Problems

  1. I do not want invalid requests to lead to exceptions on the server side, as happens with http requests.
  2. I want to get detaild error in response to an upgrade request instead of an empty 403 response. I’m sure this is technically possible, but it might be against the websocket spec.

Operating System

Windows

Operating System Details

No response

FastAPI Version

0.68.0

Python Version

Python 3.9.5

Additional Context

websockets 9.1 (pip install -U websockets)

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:5

github_iconTop GitHub Comments

5reactions
eboddingtoncommented, Sep 22, 2021

I am having the same issue and agree with @Danstiv’s conclusion.

From what I recall, the WebSocket spec says that the server should respond with 403 if the path is unexpected. Try, for example, connecting to some random path that you haven’t specified in your FastAPI code - it will respond with 403. So I think that is expected.

What isn’t expected is the server raising exceptions. FastAPI provides the ability to specify input parameters and automatically validates them. This happens outside the scope of a path operation function, so from my eyes if it is raising an exception there then that is a bug as there’s no way for us to catch it. It seems like this should either be handled and the 403 response given for us, or maybe this functionality should be unavailable for WebSocket endpoints.

As it stands, I’m not sure how to proceed as there seems to be no way to prevent clients being able to trigger exceptions on the server (in my case, the parameter is an int so passing an invalid integer can trigger the exception).

3reactions
Danstivcommented, Aug 17, 2021

Code

from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Query
from typing import Optional


app = FastAPI()


@app.get('/test1')
async def test1(value: Optional[int] = Query(1)):
    return 'msg'*value


@app.websocket('/test2')
async def test2(websocket: WebSocket, value: Optional[int] = Query(1)):
    await websocket.accept()
    try:
        while True:
            message = await websocket.receive_text()
            message = message*value
            await websocket.send_text(message)
    except WebSocketDisconnect:
        pass

Console 1

D:\temp>uvicorn --no-use-colors ws_test:app
INFO:     Started server process [52612]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Example 1

console 2

C:\Users\Danstiv>curl http://localhost:8000/test1?value=3
"msgmsgmsg"

Console 1

INFO:     127.0.0.1:49950 - "GET /test1?value=3 HTTP/1.1" 200 OK

Console 2

C:\Users\Danstiv>curl http://localhost:8000/test1?value=abc
{"detail":[{"loc":["query","value"],"msg":"value is not a valid integer","type":"type_error.integer"}]}

Console 1

INFO:     127.0.0.1:49952 - "GET /test1?value=abc HTTP/1.1" 422 Unprocessable Entity

Example 2

console 2

C:\Users\Danstiv>python -m websockets ws://localhost:8000/test2?value=3
Connected to ws://localhost:8000/test2?value=3.
> test
< testtesttest

ctrl+c

Connection closed: code = 1000 (OK), no reason.

Console 1

INFO:     ('127.0.0.1', 49960) - "WebSocket /test2" [accepted]

Console 2

C:\Users\Danstiv>python -m websockets ws://localhost:8000/test2?value=abc
Failed to connect to ws://localhost:8000/test2?value=abc: server rejected WebSocket connection: HTTP 403.

Console 1

INFO:     ('127.0.0.1', 49987) - "WebSocket /test2" 403
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "c:\python39\lib\site-packages\uvicorn\protocols\websockets\websockets_impl.py", line 171, in run_asgi
    result = await self.app(self.scope, self.asgi_receive, self.asgi_send)
  File "c:\python39\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 59, in __call__
    return await self.app(scope, receive, send)
  File "c:\python39\lib\site-packages\fastapi\applications.py", line 208, in __call__
    await super().__call__(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\applications.py", line 112, in __call__
    await self.middleware_stack(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\middleware\errors.py", line 146, in __call__
    await self.app(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\exceptions.py", line 58, in __call__
    await self.app(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\routing.py", line 580, in __call__
    await route.handle(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "c:\python39\lib\site-packages\starlette\routing.py", line 68, in app
    await func(session)
  File "c:\python39\lib\site-packages\fastapi\routing.py", line 264, in app
    raise WebSocketRequestValidationError(errors)
fastapi.exceptions.WebSocketRequestValidationError: 1 validation error for WebSocket
query -> value
  value is not a valid integer (type=type_error.integer)

Explanation

In example 1, server never raise exceptions, and if validation fails, server sends validation error to the client. In example2 server raise exception, if validation fails, and client receives empty 403 response, that do not contain useful information about validation error. I don’t want a request from the client to be able to raise exceptions on the server, is it possible to avoid raising exceptions? Also, ideally, I would like to return a response to invalid requests containing information about what exactly is wrong.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Exceptions - websockets 10.4 documentation
Raised when a handshake request or response is malformed. Raised when the Origin header in a request isn't allowed.
Read more >
Handling Errors - FastAPI
When a request contains invalid data, FastAPI internally raises a RequestValidationError . And it also includes a default exception handler for it.
Read more >
Handling operation errors - Apollo GraphQL Docs
These are errors related to the server-side execution of a GraphQL operation. They include: Syntax errors (e.g., a query was malformed); Validation errors...
Read more >
API with NestJS #4. Error handling and data validation
Therefore, we can clean up our code by not having to define the message every time we want to throw an error. NestJS...
Read more >
Errors - discord.js Guide
API Errors or DiscordAPIErrors are thrown by the Discord API when an invalid request carries out. API Errors can be mostly diagnosed using ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found