3.x Changelog

3.0.0

Released: 2364-01-27

Features

Default copy_scope to True for ASGI handlers#breaking

Every asgi handler, mounted or not, now receives a copy of the ASGI scope, so the wrapped application can no longer add, replace or remove top-level scope keys seen by the outer application. The default was None, which passed the original scope through, warned at registration and warned again when the wrapped application mutated the scope; both warnings are gone.

The copy is shallow, so nested values such as scope["state"] are still shared with the outer scope.

Set copy_scope=False where the wrapped application is meant to communicate with the outer application through the scope, for example by writing scope["user"] for outer middleware to read, or by calling Request.set_session or Request.clear_session. All of these now fail silently.

References: https://github.com/litestar-org/litestar/pull/5164

Remove support for declaring DTOField via Pydantic’s Field.extra#breaking

Declaring a DTOField through Pydantic’s Field.extra, that is Field(**dto_field("read-only")), is no longer supported. The mark was already ignored in 2.x and only emitted a deprecation warning. Remove the declaration and use Annotated[int, DTOField(mark="read-only")] instead; leaving it in place can make OpenAPI schema generation fail.

References: https://github.com/litestar-org/litestar/pull/5163

Require “reserved kwargs” to be declared with their type#breaking

Declaring the “reserved kwargs” body, cookies, data, headers, query, request, scope and socket without the type listed below, or with a different one, raises ImproperlyConfiguredException at startup.

For this, add the types RawBody, QueryParams and RequestCookies, and the body marker RequestBody, which declares a body of the default media type, application/json.

Parameter

Type

body

RawBody

cookies

RequestCookies

data

A body marker, e.g. JSONBody or RequestBody

headers

Headers

query

QueryParams

request

The request class configured for the handler, or one of its base classes, e.g. Request

scope

Scope

socket

The websocket class configured for the handler, or one of its base classes, e.g. WebSocket

headers receives the request’s Headers. Before, headers: dict[str, str] received a dict and headers: Headers failed validation with a 400 - Bad Request.

In route handlers, cookies, headers, query and scope can also be accessed through the request, e.g. request.headers.

References: https://github.com/litestar-org/litestar/pull/5154

Remove support for implicitly declared request parameters#breaking

Remove the deprecated implicit request parameter declaration. All request parameters must now be explicitly marked (e.g. with FromQuery).

Deprecated

Use instead

def h(name: str) (implicit query parameter)

def h(name: FromQuery[str])

def h(user_id: int) (implicit path parameter)

def h(user_id: FromPath[int])

Annotated[str, Parameter(query="alias")]

Annotated[str, QueryParameter(name="alias")]

Annotated[str, Parameter(header="X-API-KEY")]

Annotated[str, HeaderParameter(name="X-API-KEY")]

Annotated[str, Parameter(cookie="session")]

Annotated[str, CookieParameter(name="session")]

name: str = Parameter(...) (default-value style)

name: Annotated[str, QueryParameter(...)]

See also

Parameters

References:

Add support for leeway parameter in JWT security backends#breaking

Add support for leeway parameter in JWT security backends, which allows set a number of potential seconds as a clock error for expired tokens.

Token.decode and Token.decode_payload now take a leeway argument. Custom token classes overriding either method must accept it and forward to super(), otherwise decoding raises TypeError.

References: https://github.com/litestar-org/litestar/issues/4584, https://github.com/litestar-org/litestar/pull/5037

Return HTTP 413 when the multipart form part limit is exceeded#breaking

Exceeding the configured multipart form part limit now returns 413 Request Entity Too Large instead of 400 Bad Request.

References: https://github.com/litestar-org/litestar/issues/4439, https://github.com/litestar-org/litestar/pull/4990

Remove the RapiDoc OpenAPI UI plugin#breaking

The RapidocRenderPlugin has been removed due to lack of upstream maintenance.

Applications using RapidocRenderPlugin should migrate to another bundled UI plugin, such as ScalarRenderPlugin (the default), SwaggerRenderPlugin, RedocRenderPlugin or StoplightRenderPlugin.

References: https://github.com/litestar-org/litestar/pull/4977

Migrate CSRFMiddleware to ASGIMiddleware#breaking

CSRFMiddleware has been moved from the legacy MiddlewareProtocol base to ASGIMiddleware, as part of migrating all built-in middleware off the legacy bases. It now lives in litestar.middleware._internal. This removes the litestar.middleware.csrf module, including the generate_csrf_token and generate_csrf_hash helpers, from the public API.

Applications that configure CSRF protection through CSRFConfig are unaffected, since Litestar constructs the middleware itself, via a from_config classmethod on the middleware.

Two behavioural changes for excluded routes:

  • exclude patterns are now matched against the handler’s path template (e.g. /user/{user_id:int}) at startup, instead of the request path (e.g. /user/1) at runtime. Patterns targeting literal paths keep working; patterns written to match expanded path parameters must be rewritten against the template.

  • Handlers excluded via exclude or exclude_from_csrf now bypass the middleware entirely at startup rather than per request.

References: https://github.com/litestar-org/litestar/issues/4009, https://github.com/litestar-org/litestar/pull/4998

Migrate ResponseCacheMiddleware to ASGIMiddleware#breaking

ResponseCacheMiddleware has been moved from the legacy AbstractMiddleware base to ASGIMiddleware, as part of migrating all built-in middleware off the legacy bases. It now lives in litestar.middleware._internal, removing it from the public API.

Note

The move into litestar.middleware._internal does not affect usage: the middleware was only ever applied internally by Litestar to begin with.

Applications that configure response caching through ResponseCacheConfig and the handler-level cache argument are unaffected – Litestar constructs the middleware itself.

Code composing the middleware directly into an ASGI stack must drop the app argument, pass the settings as keyword arguments rather than a ResponseCacheConfig, and apply the instance to the next ASGI app:

# before
middleware = ResponseCacheMiddleware(app=next_app, config=response_cache_config)

# after
middleware = ResponseCacheMiddleware(
    default_expiration=response_cache_config.default_expiration,
    key_builder=response_cache_config.key_builder,
    store=response_cache_config.store,
    cache_response_filter=response_cache_config.cache_response_filter,
)
asgi_app = middleware(next_app)

Since ASGIMiddleware.__call__ returns a closure rather than the middleware instance, a middleware stack can no longer be introspected by walking the .app attribute of each layer.

References: https://github.com/litestar-org/litestar/issues/4009, https://github.com/litestar-org/litestar/pull/4953

Migrate CORSMiddleware to ASGIMiddleware#breaking

CORSMiddleware has been moved from the legacy AbstractMiddleware base to ASGIMiddleware, as part of migrating all built-in middleware off the legacy bases.

Applications that configure CORS through CORSConfig are unaffected – Litestar constructs the middleware itself.

Code instantiating the middleware directly must drop the app argument, pass the settings as keyword arguments rather than a CORSConfig, and apply the instance to the next ASGI app:

# before
middleware = CORSMiddleware(app=next_app, config=cors_config)

# after
middleware = CORSMiddleware(allow_origins=cors_config.allow_origins, ...)
asgi_app = middleware(next_app)

The settings are copied on construction, so mutating CORSConfig after the application has been created no longer affects the middleware.

Since ASGIMiddleware.__call__ returns a closure rather than the middleware instance, a middleware stack can no longer be introspected by walking the .app attribute of each layer.

References: https://github.com/litestar-org/litestar/issues/4009, https://github.com/litestar-org/litestar/pull/4952

Move httpx to the testing extra#breaking

httpx, used by the test clients, has been moved from the default dependencies to the litestar[testing] extra. It remains included in litestar[full].

Importing anything from litestar.testing without the extra installed now raises a MissingDependencyException naming the extra to install.

See also

Testing

References: https://github.com/litestar-org/litestar/pull/4950

Require rich-click>=1.9#breaking

The litestar[cli] extra now requires rich-click>=1.9. rich-click was previously capped at <1.9, which silently disabled the CLI’s star-box theme: the underlying theme option only exists from 1.9 onwards, and assigning an unknown setting is ignored rather than raising.

The CLI configuration has been migrated off the options 1.9 deprecates (use_markdown / use_rich_markup in favour of text_markup, and show_metavars_column / append_metavars_help in favour of options_table_column_types / options_table_help_sections).

The layout of the help output is preserved: option tables keep the long form in the first column, the * marker on required parameters, and the metavar appended directly after the help text. Colours do change, as the star-box theme now takes effect for the first time.

This only affects applications that additionally pin rich-click<1.9 themselves.

See also

CLI

References: https://github.com/litestar-org/litestar/pull/4951

Move click, rich and rich-click to the cli extra#breaking

click, rich and rich-click, which power the CLI, have been moved from the default dependencies to the litestar[cli] extra. They remain included in litestar[standard] and litestar[full].

Installing litestar on its own therefore no longer provides the litestar command. Invoking it without the extra now raises a MissingDependencyException naming the extra to install.

See also

CLI

References: https://github.com/litestar-org/litestar/pull/4949

Remove deprecated litestar.contrib namespace#breaking

Remove the deprecated litestar.contrib namespace. Integrations previously available from this namespace have moved to their replacement modules.

References: https://github.com/litestar-org/litestar/issues/4720

Remove deprecated declaration of metadata through default values#breaking

Remove declaring parameter and body metadata through default values and setting defaults inside markers.

  • The default keyword has been removed from Parameter and Body, along with the underlying KwargDefinition.default field. Passing it now raises a TypeError.

  • Using a QueryParameter / HeaderParameter / PathParameter / CookieParameter or Body marker as a parameter default value (for example data: Foo = Body(...) or param: int = QueryParameter(...)) now raises an exception

Migration

  • If metadata is declared on the marker, move the marker into Annotated:

    • data: Foo = Body(min_items=2) becomes data: Annotated[Foo, Body(min_items=2)]

    • param: int = QueryParameter(gt=1) becomes param: Annotated[int, QueryParameter(gt=1)]

    For the unconstrained case the dedicated markers can be used as a shorthand, e.g. JSONBody[Foo], MsgPackBody[Foo], MultipartBody[Foo] or URLEncodedBody[Foo] for bodies, and FromQuery[int], FromHeader[int], FromCookie[int] or FromPath[int] for parameters.

  • Hoist defaults set inside Annotated into the parameter default: param: Annotated[int, QueryParameter(default=5)] becomes param: Annotated[int, QueryParameter(...)] = 5.

See also

Parameters

References: https://github.com/litestar-org/litestar/pull/4819

Remove deprecated params.Dependency and params.DependencyKwarg#breaking

Remove params.Dependency and params.DependencyKwarg that were deprecated in 2.23.0.

Migration

  • Replace foo: int = Dependency() with foo: NamedDependency[int]

  • Replace foo: Annotated[int, Dependency(default=1)] by hoisting the default into the parameter default: foo: NamedDependency[int] = 1

  • Replace foo: Annotated[int, Dependency(skip_validation=True) with foo: NamedDependency[SkipValidation[int]]

References: https://github.com/litestar-org/litestar/pull/4818

Drop support for Pydantic 1#breaking

Drop support for Pydantic 1.

Pydantic 1 is no longer supported or tested against. No changes required for applications already using Pydantic 2. Applications using Pydantic 1 or a mix between 1 and 2 should upgrade to Pydantic 2.

References:

Add ping_interval to ServerSentEvent for keepalive pings#

Added optional ping_interval parameter to ServerSentEvent that sends SSE comment keepalive pings at the specified interval to prevent connection timeouts from reverse proxies or clients.

References: https://github.com/litestar-org/litestar/issues/4082, https://github.com/litestar-org/litestar/pull/4623

Remove logging config and related constructs#breaking

Remove Litestar’s logging configuration, and related modules. In particular, the following have been removed:

  • Litestar.logging_config parameter

  • Litestar.logger attribute

  • Litestar.get_logger method

  • AppConfig.logging_config attribute

  • ASGIConnection.logger property

  • litestar.logging module

  • litestar.plugins.structlog module

  • litestar.middleware.lgging.LoggingMiddlewareConfig class

Aside from debugging, there is only one place where Litestar logs, which is LoggingMiddleware, which has been changed to accept a logger directly. litestar.middleware.lgging.LoggingMiddlewareConfig has also been removed, with LoggingMiddleware now accepting all configuration arguments directly.

Users that have previously relied on Litestar to configure logging, can simply use the standard logging configuration provided by their logging library of choice.

See also

Request logging

References: https://github.com/litestar-org/litestar/issues/2858,, https://github.com/litestar-org/litestar/issues/3913,, https://github.com/litestar-org/litestar/issues/3455, https://github.com/litestar-org/litestar/pull/4615

Remove default exception handler for all unhandled exceptions#breaking

Remove the default exception handler for all unhandled exceptions. Unhandled exceptions will now be re-raised, for the server to handle. “Unhandled exceptions” in this context means any exception for which no custom exception handler exist, and that does not derive from LitestarException or HTTPException.

Important

Your application’s runtime behaviour won’t change in terms of if exceptions get handled. The main difference will be that, for an unhandled exception, it will be the ASGI server that creates a 500 - Internal Server Error response, instead of Litestar

References: https://github.com/litestar-org/litestar/pull/4610

Remove all SQLAlchemy modules in favor of direct advanced-alchemy imports#breaking

Remove all SQLAlchemy functionality from Litestar. Both litestar.contrib.sqlalchemy and litestar.plugins.sqlalchemy modules have been completely removed. Users must now import directly from advanced_alchemy.extensions.litestar.

Migration: - from litestar.contrib.sqlalchemy import X → from advanced_alchemy.extensions.litestar import X - from litestar.plugins.sqlalchemy import Y → from advanced_alchemy.extensions.litestar import Y

This completes the separation of concerns, with advanced-alchemy being the sole provider of SQLAlchemy integration for Litestar.

References: https://github.com/litestar-org/litestar/pull/4340

Remove deprecated litestar.contrib.prometheus module#breaking

Remove the deprecated litestar.contrib.prometheus module. Code still using imports from this module should switch to using litestar.plugins.prometheus.

References: https://github.com/litestar-org/litestar/issues/4305, https://github.com/litestar-org/litestar/pull/4328

Make AsyncTestClient async-native#breaking

Re-implement AsyncTestClient to be async-native, i.e. use the currently running event loop to run the application, instead of running a separate event loop in a new thread. Additionally, a new AsyncWebSocketTestSession has been introduced, providing an async testing utility for WebSockets.

To ensure consisten behaviour across TestClient and AsyncTestClient, all testing utilities have been rewritten to be async first, with their synchronous counterparts proxying calls to the async implementation, which they run internally within a dedicated thread + event loop.

References: https://github.com/litestar-org/litestar/issues/1920, https://github.com/litestar-org/litestar/pull/4291

Remove deprecated plugin properties from Litestar#breaking

Remove deprecated <plugin_type>_plugins properties from Litestar.

Removed

Use instead

Litestar.openapi_schema_plugins

Litestar.plugins.openapi_schema

Litestar.cli_plugins

Litestar.plugins.cli

Litestar.serialization_plugins

Litestar.serialization.cli

References: https://github.com/litestar-org/litestar/pull/4297

Remove deprecated allow_reserved and allow_empty_value property from ResponseHeader and OpenAPIHeader#breaking

Remove the deprecated properties allow_reserved and allow_empty_value from ResponseHeader and OpenAPIHeader.

References: https://github.com/litestar-org/litestar/pull/4299

Remove deprecated traceback_line_limit parameter of LoggingConfig#breaking

The traceback_line_limit parameter of litestar.logging.config.LoggingConfig has been removed. This parameter had no effect since version 2.9.0, so it can be removed safely from applications without any change in behaviour.

References: https://github.com/litestar-org/litestar/pull/4300

Remove deprecated litestar.middleware.cors module#breaking

Remove the deprecated litestar.middleware.cors module and litestar.middleware.cors.CORSMiddleware. To configure the CORS middleware, use CORSConfig.

References: https://github.com/litestar-org/litestar/pull/4309

Remove deprecated encoded_headers parameter from ASGI response classes and to_asgi_response methods#breaking

The deprecated encoded_headers parameter has been removed from the following clases:

Existing code still using encoded_headers should be migrated to using the headers parameter instead.

References: https://github.com/litestar-org/litestar/pull/4311

Remove deprecated litestar.contrib.htmx module#breaking

Remove the deprecated litestar.contrib.htmx module. Code still using imports from this module should switch to using litestar_htmx.

Install it via litestar[htmx] extra.

References: https://github.com/litestar-org/litestar/issues/4303, https://github.com/litestar-org/litestar/pull/4316

Remove deprecated LitestarType#breaking

Remove the deprecated litestar.types.internal_types.LitestarType type alias. In its stead, type[Litestar] can be used.

References: https://github.com/litestar-org/litestar/pull/4312

Remove deprecated TemplateContext#breaking

Remove the deprecated litestar.template.base.TemplateContext type. Its usages should be replaced with collections.abc.Mapping.

References: https://github.com/litestar-org/litestar/pull/4313

Remove deprecated ASGIResponse.encoded_headers property#breaking

Remove the deprecated ASGIResponse.encoded_headers property. Instead, encode_headers() should be used.

References: https://github.com/litestar-org/litestar/pull/4314

Remove deprecated pydantic_get_unwrapped_annotation_and_type_hints#breaking

Remove the deprecated pydantic_get_unwrapped_annotation_and_type_hints function.

References: https://github.com/litestar-org/litestar/pull/4315

Remove deprecated litestar.contrib.attrs module#breaking

Remove the deprecated litestar.contrib.attrs module. Code still using imports from this module should switch to using litestar.plugins.attrs.

References: https://github.com/litestar-org/litestar/issues/4302, https://github.com/litestar-org/litestar/pull/4322

Remove deprecated litestar.contrib.jwt module#breaking

Remove the deprecated litestar.contrib.jwt module. Code still using imports from this module should switch to using litestar.security.jwt.

References: https://github.com/litestar-org/litestar/issues/4304, https://github.com/litestar-org/litestar/pull/4333

Remove deprecated litestar.contrib.repository module#breaking

Remove the deprecated litestar.contrib.repository module. Code still using imports from this module should switch to using litestar.repository.

References: https://github.com/litestar-org/litestar/issues/4307, https://github.com/litestar-org/litestar/pull/4337

Remove deprecated litestar.contrib.pydantic module#breaking

Remove the deprecated litestar.contrib.pydantic module. Code still using imports from this module should switch to using litestar.plugins.pydantic.

References: https://github.com/litestar-org/litestar/issues/4306, https://github.com/litestar-org/litestar/pull/4339

Remove deprecated module litestar/contrib/minijnja#breaking

Remove the deprecated module litestar.contrib.minijnja. This module was created with a typo in its name (minijnja instead of minijinja). Instead litestar.contrib.minijinja should be used.

References: https://github.com/litestar-org/litestar/issues/4357, https://github.com/litestar-org/litestar/pull/4357

Add round_trip parameter to PydanticPlugin#

New round_trip: bool parameter to PydanticPlugin allows serializing types like pydanctic.Json correctly.

References: https://github.com/litestar-org/litestar/issues/4349, https://github.com/litestar-org/litestar/pull/4350

Remove deprecated litestar.contrib.minijinja.minijinja_from_state function#breaking

Remove the deprecated litestar.contrib.minijinja.minijinja_from_state function. Instead use a callable that receives the minijinja State object as first argument.

References: https://github.com/litestar-org/litestar/issues/4356, https://github.com/litestar-org/litestar/pull/4355

Remove deprecated litestar.contrib.piccolo module#breaking

Use litestar[piccolo] extra installation target and litestar_piccolo plugin instead: https://github.com/litestar-org/litestar-piccolo

References: https://github.com/litestar-org/litestar/issues/4364, https://github.com/litestar-org/litestar/pull/4363

Move litestar.contrib.opentelemetry to litestar.plugins.opentelemetry and add upstream parity#breaking

Move the OpenTelemetry integration from litestar.contrib.opentelemetry to litestar.plugins.opentelemetry. Code still using imports from the old module should switch to the new path.

OpenTelemetryConfig has been brought in line with the upstream opentelemetry.instrumentation.asgi.OpenTelemetryMiddleware configuration, adding client_request_hook_handler, client_response_hook_handler, tracer, exclude, exclude_opt_key, exclude_urls_env_key, exclude_spans, http_capture_headers_server_request, http_capture_headers_server_response, and http_capture_headers_sanitize_fields.

Also fixes a KeyError: 'route_handler' raised on every request when exclude_opt_key was set, and corrects the OpenTelemetryHookHandler signature to accept the span, ASGI scope, and ASGI message as documented by upstream.

References: https://github.com/litestar-org/litestar/issues/4466,, https://github.com/litestar-org/litestar/issues/4468, https://github.com/litestar-org/litestar/pull/4691

Zero cost excluded middlewares#breaking

Middlewares inheriting from ASGIMiddleware will now have zero runtime cost when they are excluded e.g. via the scope or exclude_opt_key options.

Previously, the base middleware was always being invoked for every request, evaluating the exclusion criteria, and then calling the user defined middleware functions. If a middleware had defined scopes = (ScopeType.HTTP,), it would still be called for every request, regardless of the scope type. Only for requests with the type HTTP, it would then call the user’s function.

Note

This behaviour is still true for the legacy AbstractMiddleware

With zero cost exclusion, the exclusion is being evaluated statically. At app creation time, when route handlers are registered and their middleware stacks are being built, a middleware that is to be excluded will simply not be included in the stack.

Note

Even though this change is marked as breaking, no runtime behaviour difference is expected. Some test cases may break though if they relied on the fact that the middleware wrapper created by ASGIMiddleware was always being called

References:

Support for typing.ReadOnly in typed dict schemas#

Support unwrapping ReadOnly type in schemas like:

from typing import ReadOnly, TypedDict

class User(TypedDict):
    id: ReadOnly[int]

typing_extensions.ReadOnly should be used for python versions <3.13.

References: https://github.com/litestar-org/litestar/issues/4423, https://github.com/litestar-org/litestar/pull/4424

Add should_bypass_for_scope to ASGIMiddleware to allow excluding middlewares dynamically#

Add a new attribute should_bypass_for_scope; A callable which takes in a Scope and returns a boolean to indicate whether to bypass the middleware for the current request.

References: https://github.com/litestar-org/litestar/pull/4441

Bugfixes

DI: Correctly set Pydantic dependency fields as required without a default#

Fix a bug, causing a Pydantic model used as a dependency to fail at startup with Missing declaration for parameter for each of its fields without a marker, since PydanticDIPlugin marked them as dependencies only in the signature, not in the type hints used by the rest of the machinery.

References: https://github.com/litestar-org/litestar/pull/5195

DI: Correctly set msgspec.Struct dependency fields as required without a default#

Fix a bug that would implicitly give a field of an msgspec.Struct used as a dependency a default of msgspec.NODEFAULT and mark it as optional. Struct fields without a default are now correctly marked as required.

References: https://github.com/litestar-org/litestar/pull/5194

Parse every cookie header of a request#breaking

Only the first cookie header was parsed into cookies. Over HTTP/2 and HTTP/3 the header may repeat, and browsers send one per cookie, so every cookie but those in the first header went missing. All cookie headers are now parsed, joined with the "; " delimiter RFC 9113 and RFC 9114 prescribe.

A cookie name sent more than once now keeps its first value, where it kept its last one before, following the RFC 6265 ordering that serialises the cookie of the longer path first. Scope a cookie holding a session or a CSRF token with the __Host- prefix so that a cookie of the same name, set under a longer path, cannot take precedence over it. cookies of LoggingMiddleware’s response log likewise holds the first of several set-cookie headers of one name, not the last.

References: https://github.com/litestar-org/litestar/issues/5177

Restore a session split across several cookies over HTTP/2 and HTTP/3#

A session too large for one cookie is stored across several, named session-0, session-1 and so on. Over HTTP/2 and HTTP/3 only the chunks carried by the first cookie header were read, and ClientSideSessionBackend yields an empty session when it cannot decode the value in full, so such a session was silently dropped and the user logged out. Every chunk is now read.

References: https://github.com/litestar-org/litestar/issues/5177

Send repeated headers separately from the test client#breaking

The test client merged headers of the same name into one comma-joined header, which is invalid for cookie, and left a request carrying a repeated header impossible to simulate. Repeated headers now reach the application as they were given, on requests and on websocket_connect(), which dropped all but the last of them.

A test passing a header of the same name twice is affected: the application now receives the two values it was given, rather than one comma-joined value. Header values are also passed on as the bytes they were given in, where they were decoded and re-encoded as UTF-8 before.

References: https://github.com/litestar-org/litestar/issues/5177

Fix Optional / Any + required=False + without default parameters receiving Empty#breaking

Fix a bug that would cause a request parameter typed Optional or Any, and declared without a default and annotated as required=False to receive the Empty sentinel when that parameter was missing from the request.

A parameter annotated required=False that is consumed by any handler or dependency, and does not have a default, now raises ImproperlyConfiguredException at startup.

This includes a marker for a parameter also declared as a layer parameter with required=False. Layer parameters that are not consumed are not affected by this.

References: https://github.com/litestar-org/litestar/pull/5185

Fix ModuleNotFoundError when Pydantic or attrs are not installed#

Fix a bug that would raise ModuleNotFoundError when attrs or Pydantic were not installed.

References: https://github.com/litestar-org/litestar/issues/5142

ServerSideSessionBackend: Prevent storing empty sessions and session cookies for every request#

Fix a bug where ServerSideSessionBackend would store an empty session under a new session ID and set a session cookie for every request, even when that request did not have a session cookie attached.

The fixed backend will now only store a session if the client has a session cookie set. Empty sessions for a request with a session cookie will now also be deleted reliably.

References: https://github.com/litestar-org/litestar/issues/5182, https://github.com/litestar-org/litestar/pull/5172

Apply allow_symlinks_outside_directory=False to every part of a static file’s path#

Fix a bug that would resolve symlinks only for the directory path, not the file path, when allow_symlinks_outside_directory=False. The whole path is now always resolved.

References: https://github.com/litestar-org/litestar/pull/5181

Match mounted ASGI apps on path segments#breaking

A mount matched any path merely starting with its path string, so a mount on /admin served /administrator, handing the mounted app istrator/ as its path. A mount now matches only its own path and sub-paths of it, /admin and /admin/..., and / still mounts everything that no more specific mount claims.

Of several mounts matching a path, the longest one now receives the request, so a mount nested under another, e.g. /admin/inner under /admin, is reachable. This also fixes mounts sharing a prefix shadowing one another, and mount paths containing regex metacharacters, which raised 500 - Internal Server Error or failed application startup.

An application relying on a mount absorbing sibling paths that merely share its prefix now receives 404 - Not Found for them. Mount the application one path segment higher, or declare the sibling path as a further path of the mount, e.g. @asgi(["/admin", "/administrator"], is_mount=True).

References: https://github.com/litestar-org/litestar/issues/5173, https://github.com/litestar-org/litestar/pull/5178

Declare socket in the original signature of a websocket_stream handler#

A websocket_stream handler whose function takes no socket parameter adds one to its parsed_fn_signature, but only to its parameters, so its original_signature lacked a parameter its parameters declared. It now declares socket in both.

References: https://github.com/litestar-org/litestar/pull/5161

Read constraint metadata on data as on any other parameter#

A data parameter annotated with metadata, e.g. data: Annotated[int, annotated_types.Gt(0)], was interpreted as a request body without a body marker, which got around the check of data being missing a marker

References: https://github.com/litestar-org/litestar/pull/5157

Only read parameters marked as path parameters from the path#breaking

A parameter whose name matched a path segment was read from the path even when it was marked with FromQuery, FromHeader, FromCookie or the corresponding Parameter kwarg, and was documented as a path parameter. Such a parameter is now read from, and documented at, the location it is marked with.

A parameter named like a path segment that declares no location, the deprecated inferred path parameter style, now raises ImproperlyConfiguredException when the application is created. Declare it with FromPath.

References: https://github.com/litestar-org/litestar/pull/5128

Fix TypeError when generating a schema for a union of enums#

Annotating a handler with a union whose members are all enums (e.g. Color | Size) crashed schema generation with TypeError: issubclass() arg 1 must be a class, as the union was dispatched to the enum schema handler. Such unions are now documented as a oneOf with a component schema per enum.

References: https://github.com/litestar-org/litestar/pull/4997

Stop caching responses of handlers that do not enable caching#

ResponseCacheMiddleware is applied per route, so a handler that never set cache still had its responses written to the store when it shared a path with a handler that did – for example a POST handler on a path whose GET handler sets cache=True. Those entries were written on every request, without an expiry, and were never read back, since responses are only served from the cache for handlers that enable it.

The middleware now passes such requests through untouched.

References: https://github.com/litestar-org/litestar/pull/4953

Harden the PsycoPg channels listener#

Serialize subscription changes and consume notifications in a background task so dynamic LISTEN and UNLISTEN operations no longer contend with the listener. Psycopg 3.2.4 or newer is now required for development and documentation builds.

References:

Fix OpenAPI schema incorrectly marking nullable required fields as not required#

Fields typed as T | None (nullable) without a default value are now correctly included in the OpenAPI schema’s required array. Previously, Litestar conflated nullability (the type can be None) with optionality (having a default value), causing the generated schema to diverge from the OpenAPI 3.1.0 specification.

This affected all model types: dataclasses, msgspec Structs, attrs classes, and Pydantic models. For example, a field name: int | None with no default was previously excluded from required; it is now correctly included.

Runtime request validation behavior is unchanged — nullable parameters without defaults were already enforced as required at the validation layer.

References: https://github.com/litestar-org/litestar/issues/4673, https://github.com/litestar-org/litestar/pull/4687

Fix raise_server_exceptions flag in test clients#

Fix the raise_server_exceptions. This flag has been non-functional (i.e. did not change the behaviour at all) since version 1. This is now fixed, and unhandled exceptions will be re-raised by the test client

References: https://github.com/litestar-org/litestar/pull/4610

Fix swallowing of unhandled exceptions in WebSocket client#

Fix a bug that could lead to unhandled exceptions raised within websocket handlers, and not derived from LitestarException or HTTPException to be silently swallowed. This would lead to a closing of the WebSocket connection without any indication of an exception

References: https://github.com/litestar-org/litestar/pull/4610

Change Optional to NotRequired for pydantic fields with default_factory#

Now, in the OpenAPI schema, pydantic fields with default_factory are displayed as non-null and not required. Previously, this fields was nullable and not required.

References: https://github.com/litestar-org/litestar/issues/4294, https://github.com/litestar-org/litestar/pull/4347

Fix KeyError when ClassVar exists on msgspec Struct#

Fix a bug in MsgspecDTO where a KeyError was raised if a msgspec.Struct contained a ClassVar. ClassVars are now correctly skipped when generating field definitions.

References: https://github.com/litestar-org/litestar/pull/4665