Skip to content

Empty Content-Type header causes 500 Internal Server Error on schema register #1267

@e11it

Description

@e11it

What happened?

Registering an Avro schema with a missing/empty Content-Type header causes 500 Internal Server Error instead of returning a client error.

What did you expect to happen?

Return 415 Unsupported Media Type.

What else do we need to know?

Tested with ghcr.io/aiven-open/karapace:latest.
The request can be reproduced with curl by explicitly sending no Content-Type:

curl -i --http1.1 -X POST 'http://localhost:8081/subjects/test4/versions?normalize=true' \
  -H 'Authorization: Basic YWtocTpha2hxX3Bhc3N3b3Jk' \
  -H 'User-Agent: vscode-restclient' \
  -H 'Content-Type:' \
  --data-binary '{"schema":"{\"type\":\"record\",\"name\":\"test\",\"namespace\":\"testing\",\"fields\":[{\"type\":\"string\",\"name\":\"field1\"}]}"}'

Actual response:

HTTP/1.1 500 Internal Server Error

Internal Server Error

This is also reproducible from VSCode REST Client, which may send request body without Content-Type.

From logs, the failure happens while rendering validation error response (TypeError: Object of type bytes is not JSON serializable) rather than returning 415.

Full trace log
uvicorn.access          MainThread      INFO            185.15.59.224:41580 - "POST /subjects/test4/versions?normalize=true HTTP/1.1" 500
uvicorn.error           MainThread      ERROR           Exception in ASGI application
Traceback (most recent call last):
  File "/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 416, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/fastapi/applications.py", line 1159, in __call__
    await super().__call__(scope, receive, send)
  File "/venv/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 416, in _sentry_patched_asgi_app
    return await middleware(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/sentry_sdk/integrations/asgi.py", line 178, in _run_asgi3
    return await self._run_app(scope, receive, send, asgi_version=3)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/sentry_sdk/integrations/asgi.py", line 347, in _run_app
    raise exc from None
  File "/venv/lib/python3.12/site-packages/sentry_sdk/integrations/asgi.py", line 336, in _run_app
    return await self.app(
           ^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/starlette/applications.py", line 107, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/venv/lib/python3.12/site-packages/prometheus_fastapi_instrumentator/middleware.py", line 177, in __call__
    raise exc
  File "/venv/lib/python3.12/site-packages/prometheus_fastapi_instrumentator/middleware.py", line 175, in __call__
    await self.app(scope, receive, send_wrapper)
  File "/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 191, in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__
    self.gen.throw(value)
  File "/venv/lib/python3.12/site-packages/starlette/_utils.py", line 87, in collapse_excgroups
    raise exc
  File "/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/dependency_injector/wiring.py", line 1187, in _patched
    return await fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/karapace/api/telemetry/middleware.py", line 39, in telemetry_middleware
    raise exc
  File "/venv/lib/python3.12/site-packages/karapace/api/telemetry/middleware.py", line 32, in telemetry_middleware
    response: Response = await call_next(request)
                         ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 168, in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
  File "/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 144, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 191, in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__
    self.gen.throw(value)
  File "/venv/lib/python3.12/site-packages/starlette/_utils.py", line 87, in collapse_excgroups
    raise exc
  File "/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/karapace/api/middlewares/__init__.py", line 68, in set_content_types
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 168, in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
  File "/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 144, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 63, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    raise exc
  File "/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
    await app(scope, receive, sender)
  File "/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
  File "/venv/lib/python3.12/site-packages/starlette/routing.py", line 716, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/venv/lib/python3.12/site-packages/starlette/routing.py", line 736, in app
    await route.handle(scope, receive, send)
  File "/venv/lib/python3.12/site-packages/starlette/routing.py", line 290, in handle
    await self.app(scope, receive, send)
  File "/venv/lib/python3.12/site-packages/fastapi/routing.py", line 134, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 59, in wrapped_app
    response = await handler(conn, exc)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/karapace/api/http_handlers/__init__.py", line 28, in validation_exception_handler
    return JSONResponse(
           ^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/starlette/responses.py", line 189, in __init__
    super().__init__(content, status_code, headers, media_type, background)
  File "/venv/lib/python3.12/site-packages/starlette/responses.py", line 46, in __init__
    self.body = self.render(content)
                ^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.12/site-packages/starlette/responses.py", line 192, in render
    return json.dumps(
           ^^^^^^^^^^^
  File "/usr/local/lib/python3.12/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
          ^^^^^^^^^^^
  File "/usr/local/lib/python3.12/json/encoder.py", line 200, in encode
    chunks = self.iterencode(o, _one_shot=True)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/json/encoder.py", line 258, in iterencode
    return _iterencode(o, 0)
           ^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/json/encoder.py", line 180, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type bytes is not JSON serializable

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions