Repository: litestar-org/litestar (python)

Issue:
Docs: Exceptions are not always JSON by default
### Summary

The [documentation states](https://docs.litestar.dev/latest/usage/exceptions.html#exception-handling) that
> Litestar handles all errors by default by transforming them into JSON responses.

Since #2131 this is not *entirely* true as exceptions will retain the `MediaType` of the route handler.

Consider the following endpoint:
```py
@get(sync_to_thread=False)
def handler(q: int) -> str:
    raise ValueError
```
The `MediaType` of this handler will be `MediaType.TEXT` by default.
When let's say a `ValidationException` is raised due to wrong/missing query param or an internal error is raised, etc. the resulting response will be a JSON error message but in the response the `content-type` header will be set to `text/plain`.

If a client expects JSON on errors --by for example checking the status code-- as declared in the documentation, then JSON parsing *might* fail as the response `content-type` header does not indicate JSON.

Pull request description:
docs: show exceptions raised aren't always JSON
<!--
By submitting this pull request, you agree to:
- follow [Litestar's Code of Conduct](https://github.com/litestar-org/.github/blob/main/CODE_OF_CONDUCT.md)
- follow [Litestar's contribution guidelines](https://github.com/litestar-org/.github/blob/main/CONTRIBUTING.md)
- follow the [PSFs's Code of Conduct](https://www.python.org/psf/conduct/)
-->
## Description

-

<!--
Please add in issue numbers this pull request will close, if applicable
Examples: Fixes #4321 or Closes #1234

Ensure you are using a supported keyword to properly link an issue:
https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword
-->
## Closes

fixes #3062 


Patch:
diff --git a/docs/examples/exceptions/implicit_media_type.py b/docs/examples/exceptions/implicit_media_type.py
new file mode 100644
index 000000000..b8a945ade
--- /dev/null
+++ b/docs/examples/exceptions/implicit_media_type.py
@@ -0,0 +1,6 @@
+from litestar import get
+
+
+@get(sync_to_thread=False)
+def handler(q: int) -> str:
+    raise ValueError
diff --git a/docs/usage/exceptions.rst b/docs/usage/exceptions.rst
index 2f3cc6a83..68f09e992 100644
--- a/docs/usage/exceptions.rst
+++ b/docs/usage/exceptions.rst
@@ -75,6 +75,12 @@ Litestar handles all errors by default by transforming them into **JSON response
 :class:`~litestar.exceptions.http_exceptions.HTTPException`, the responses will include the appropriate ``status_code``.
 Otherwise, the responses will default to ``500 - "Internal Server Error"``.
 
+
+The following handler for instance will default to ``MediaType.TEXT`` so the exception will be raised as text.
+
+.. literalinclude:: /examples/exceptions/implicit_media_type.py
+    :language: python
+
 You can customize exception handling by passing a dictionary, mapping either status codes
 or exception classes to callables. For example, if you would like to replace the default
 exception handler with a handler that returns plain-text responses you could do this:


Test patch:
diff --git a/tests/e2e/test_exception_handlers/test_exception_handlers.py b/tests/e2e/test_exception_handlers/test_exception_handlers.py
index b214e0931..0dc0a9312 100644
--- a/tests/e2e/test_exception_handlers/test_exception_handlers.py
+++ b/tests/e2e/test_exception_handlers/test_exception_handlers.py
@@ -1,6 +1,7 @@
 from typing import TYPE_CHECKING, Type
 
 import pytest
+from docs.examples.exceptions.implicit_media_type import handler as implicit_media_type_handler
 
 from litestar import Controller, Request, Response, Router, get
 from litestar.exceptions import (
@@ -83,3 +84,11 @@ def test_exception_handler_with_custom_request_class() -> None:
 
     with create_test_client([index], exception_handlers={Exception: handler}, request_class=CustomRequest) as client:
         client.get("/")
+
+
+def test_exception_handler_implicit_media_type() -> None:
+    with create_test_client([implicit_media_type_handler]) as client:
+        response = client.get("/", params={"q": 1})
+        assert response.status_code == 500
+        assert response.headers["content-type"] == "text/plain; charset=utf-8"
+        assert "ValueError" in response.text