Repository: strawberry-graphql/strawberry

Issue #3288: Malformed request with `operationName` that isn't in `query` gives 500 error: `Can't get GraphQL operation type`
Created: 2023-12-13T04:37:19Z
Describe the Bug
Strawberry throws an uncaught RuntimeError("Can't get GraphQL operation type") resulting in a 500 server error for a request that provides an operationName that isn't in the query, e.g.
{
  "query":"query test { example }",
  "operationName":"different"
}
This is an invalid/malformed request (that is, a client problem), and thus should at most be a 4xx error, or maybe a 200 with error details in the response like other sorts of errors.
Reproducer: start a server (e.g. a minimal one like the following):
cd $(mktemp -d)

python -m venv .venv
. .venv/bin/activate

pip install 'strawberry-graphql[debug-server]==0.216.1'

cat > app.py <<EOF
import strawberry

@strawberry.type
class Query:
    @strawberry.field
    @staticmethod
    def example() -> str:
        return ""

schema = strawberry.Schema(Query)
EOF

python -m strawberry server app:schema
Run a request like:
curl -i 'http://localhost:8765/graphql' -X 'POST' -H 'Content-Type: application/json' --data-binary '{"query":"query test { example }","operationName":"different"}'

The output looks like:
HTTP/1.1 500 Internal Server Error
date: Wed, 13 Dec 2023 04:33:51 GMT
server: uvicorn
content-length: 3493
content-type: text/plain; charset=utf-8

Traceback (most recent call last):
  File ".../.venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File ".../.venv/lib/python3.9/site-packages/starlette/middleware/cors.py", line 83, in __call__
    await self.app(scope, receive, send)
  File ".../.venv/lib/python3.9/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File ".../.venv/lib/python3.9/site-packages/starlette/_exception_handler.py", line 55, in wrapped_app
    raise exc
  File ".../.venv/lib/python3.9/site-packages/starlette/_exception_handler.py", line 44, in wrapped_app
    await app(scope, receive, sender)
  File ".../.venv/lib/python3.9/site-packages/starlette/routing.py", line 754, in __call__
    await self.middleware_stack(scope, receive, send)
  File ".../.venv/lib/python3.9/site-packages/starlette/routing.py", line 774, in app
    await route.handle(scope, receive, send)
  File ".../.venv/lib/python3.9/site-packages/starlette/routing.py", line 296, in handle
    await self.app(scope, receive, send)
  File ".../.venv/lib/python3.9/site-packages/strawberry/asgi/__init__.py", line 122, in __call__
    return await self.handle_http(scope, receive, send)
  File ".../.venv/lib/python3.9/site-packages/strawberry/asgi/__init__.py", line 189, in handle_http
    response = await self.run(request)
  File ".../.venv/lib/python3.9/site-packages/strawberry/http/async_base_view.py", line 185, in run
    result = await self.execute_operation(
  File ".../.venv/lib/python3.9/site-packages/strawberry/http/async_base_view.py", line 117, in execute_operation
    return await self.schema.execute(
  File ".../.venv/lib/python3.9/site-packages/strawberry/schema/schema.py", line 256, in execute
    result = await execute(
  File ".../.venv/lib/python3.9/site-packages/strawberry/schema/execute.py", line 125, in execute
    if execution_context.operation_type not in allowed_operation_types:
  File ".../.venv/lib/python3.9/site-packages/strawberry/types/execution.py", line 78, in operation_type
    return get_operation_type(graphql_document, self.operation_name)
  File ".../.venv/lib/python3.9/site-packages/strawberry/utils/operation.py", line 38, in get_operation_type
    raise RuntimeError("Can't get GraphQL operation type")

Fully pinned set of transitive dependencies
anyio==4.1.0
click==8.1.7
exceptiongroup==1.2.0
graphql-core==3.2.3
h11==0.14.0
idna==3.6
libcst==1.1.0
markdown-it-py==3.0.0
mdurl==0.1.2
mypy-extensions==1.0.0
Pygments==2.17.2
python-dateutil==2.8.2
python-multipart==0.0.6
PyYAML==6.0.1
rich==13.7.0
six==1.16.0
sniffio==1.3.0
starlette==0.33.0
strawberry-graphql==0.216.1
typer==0.9.0
typing-inspect==0.9.0
typing_extensions==4.9.0
uvicorn==0.24.0.post1


System Information

Operating system: macOS
Strawberry version (if applicable): 0.216.1

Additional Context
Thanks for strawberry! 🍓
#3120 is potentially related but that doesn't have a reproducer so I'm not 100% sure 😄

Duplicate issue #3120: Can't get GraphQL operation type error  
Created: 2023-09-25T11:41:02Z
Describe the Bug
When sending a request from a browser, the server returns a 500 status code with no error message. When sending the exact same request through Insomia, the expected response with 200 status code will come back. Token, variables, request body and all headers in the browser and Insomia are the same .

System Information

Operating system: Python docker image which based on 'linux/amd64'
Strawberry version (if applicable): 0.205.0

Additional Context
Request:
query QuizGet($project_id: UUID!, $quiz_id: UUID!, $first: Int, $skip: Int) {
  entries_get(where: { project: { id: $project_id }, id: $quiz_id }) {
    aggregation {
      total
    }
    pagination(first: $first, skip: $skip, order_by: "created_at", desc: true) {
      items {
        id
      }
    }
  }
}

Query Vars:
{
  "project_id": "1cb48220-0198-43a8-b0ea-04b1f4e144c9",
  "quiz_id": "eb3a13ce-e736-4653-9718-9c82f2235481"
}

500 status code from browser:

200 status code from Insomnia:

Error from logs which returns 500 status code:

Traceback:
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/sources/db_builder/core/fastapi/middleware.py", line 16, in __call__
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/cors.py", line 91, in __call__
    await self.simple_response(scope, receive, send, request_headers=headers)
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/cors.py", line 146, in simple_response
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    raise e
  File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 66, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 273, in app
    raw_response = await run_endpoint_function(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 190, in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/strawberry/fastapi/router.py", line 227, in handle_http_post
    return await self.run(
           ^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/strawberry/http/async_base_view.py", line 176, in run
    result = await self.execute_operation(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/strawberry/http/async_base_view.py", line 115, in execute_operation
    return await self.schema.execute(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/strawberry/schema/schema.py", line 256, in execute
    result = await execute(
             ^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/strawberry/schema/execute.py", line 120, in execute
    if execution_context.operation_type not in allowed_operation_types:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/strawberry/types/execution.py", line 78, in operation_type
    return get_operation_type(graphql_document, self.operation_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/strawberry/utils/operation.py", line 38, in get_operation_type
    raise RuntimeError("Can't get GraphQL operation type")
RuntimeError: Can't get GraphQL operation type

During handling of the above exception, another exception occurred:

  + Exception Group Traceback (most recent call last):
  |   File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/http/h11_impl.py", line 408, in run_asgi
  |     result = await app(  # type: ignore[func-returns-value]
  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "/usr/local/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
  |     return await self.app(scope, receive, send)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "/usr/local/lib/python3.11/site-packages/fastapi/applications.py", line 289, in __call__
  |     await super().__call__(scope, receive, send)
  |   File "/usr/local/lib/python3.11/site-packages/starlette/applications.py", line 122, in __call__
  |     await self.middleware_stack(scope, receive, send)
  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__
  |     raise exc
  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
  |     await self.app(scope, receive, _send)
  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 106, in __call__
  |     async with anyio.create_task_group() as task_group:
  |   File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 664, in __aexit__
  |     raise BaseExceptionGroup(
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/usr/local/lib/python3.11/site-packages/anyio/streams/memory.py", line 97, in receive
    |     return self.receive_nowait()
    |            ^^^^^^^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/anyio/streams/memory.py", line 92, in receive_nowait
    |     raise WouldBlock
    | anyio.WouldBlock
    | 
    | During handling of the above exception, another exception occurred:
    | 
    | Traceback (most recent call last):
    |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 78, in call_next
    |     message = await recv_stream.receive()
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/anyio/streams/memory.py", line 112, in receive
    |     raise EndOfStream
    | anyio.EndOfStream
    | 
    | During handling of the above exception, another exception occurred:
    | 
    | Traceback (most recent call last):
    |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 108, in __call__
    |     response = await self.dispatch_func(request, call_next)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/sources/db_builder/core/fastapi/middleware.py", line 16, in __call__
    |     response = await call_next(request)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 84, in call_next
    |     raise app_exc
    |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 70, in coro
    |     await self.app(scope, receive_or_disconnect, send_no_error)
    |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/cors.py", line 91, in __call__
    |     await self.simple_response(scope, receive, send, request_headers=headers)
    |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/cors.py", line 146, in simple_response
    |     await self.app(scope, receive, send)
    |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    |     raise exc
    |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    |     await self.app(scope, receive, sender)
    |   File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    |     raise e
    |   File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    |     await self.app(scope, receive, send)
    |   File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__
    |     await route.handle(scope, receive, send)
    |   File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 276, in handle
    |     await self.app(scope, receive, send)
    |   File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 66, in app
    |     response = await func(request)
    |                ^^^^^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 273, in app
    |     raw_response = await run_endpoint_function(
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 190, in run_endpoint_function
    |     return await dependant.call(**values)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/strawberry/fastapi/router.py", line 227, in handle_http_post
    |     return await self.run(
    |            ^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/strawberry/http/async_base_view.py", line 176, in run
    |     result = await self.execute_operation(
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/strawberry/http/async_base_view.py", line 115, in execute_operation
    |     return await self.schema.execute(
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/strawberry/schema/schema.py", line 256, in execute
    |     result = await execute(
    |              ^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/strawberry/schema/execute.py", line 120, in execute
    |     if execution_context.operation_type not in allowed_operation_types:
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/strawberry/types/execution.py", line 78, in operation_type
    |     return get_operation_type(graphql_document, self.operation_name)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/usr/local/lib/python3.11/site-packages/strawberry/utils/operation.py", line 38, in get_operation_type
    |     raise RuntimeError("Can't get GraphQL operation type")
    | RuntimeError: Can't get GraphQL operation type
    +------------------------------------

Closing comment (2025-04-10T22:59:31Z): Potentially this was addressed by #3217, per #3288 (comment)
Although, it's unclear from the issue description what the value of the operationName input is, and whether this issue is the same as #3288.