### Title: Negated directory ignore patterns inside a subdirectory `.gitignore` fail to un-ignore files

### Observed behavior
When a `.gitignore` file is placed in a subdirectory of a repository (e.g. `project/.gitignore`) and contains a rule that ignores all top-level directories using a trailing-slash pattern (`/*/`) followed by a negation rule that should re‑include one of those directories (e.g. `!/src`), files inside the explicitly un-ignored directory are **still treated as ignored**.

**Example**

`project/.gitignore`:
```
/*/
!/src
```

With that file in place:
- `project/src/main.c` → ignored (WRONG)
- `project/src/foo/foo.c` → ignored (WRONG)
- `project/README.md` → not ignored (might or might not be affected depending on other rules)
- `project/dist/foo.o` → ignored (correct)

The same set of rules placed in a **repository‑root** `.gitignore` works correctly and leaves `src/` files un‑ignored.

### Expected behavior
- `project/src/main.c` and `project/src/foo/foo.c` should **not** be ignored because the `!/src` negation explicitly re‑includes that directory.
- The directory‑only ignore pattern `/*/` should only apply to direct children of the directory containing the `.gitignore` file, and a negation that matches a directory name at that level should take priority.
- Files outside the un‑ignored directories (e.g. `project/dist/foo.o`) should remain ignored.

### Impact
This makes it impossible to use the common “ignore everything except these directories” pattern with trailing-slash directory rules inside a subdirectory `.gitignore`. Users are forced to either move all rules to the repository root or use less‑intuitive workarounds (e.g. enumerating every file), which is fragile and hard to maintain for deeply nested projects.

### Additional notes
- The problem is specific to patterns that end with `/` (intended to match directories only) in combination with a negation pattern; file‑oriented patterns (e.g. `/*` / `!src`) work correctly in the same subdirectory `.gitignore`.
- The behavior is independent of the operating system and filesystem case‑sensitivity settings.
- The fix must ensure that the “top‑level” check for a subdirectory `.gitignore` is scoped to that ignore file’s containing directory rather than the repository root, so that `!/src` correctly un‑ignores `src/` when it sits next to the `.gitignore` file.

Fix issue with directory glob ignore in subdirectories


`git_submodule_name_is_valid` currently checks for directory traversal using forward slashes (`../../evil`), but on Windows backslashes are also path separators. A submodule name using backslash traversal (`..\..\evil`) can bypass the check and escape the repository during submodule operations.

Make `git_submodule_name_is_valid` treat backslashes as path separators before validation.  Normalize backslashes to forward slashes (or otherwise ensure the same traversal rejection applies) so that a submodule name like `..\..\modules\evil` is considered invalid and rejected everywhere: loading, iteration, lookup, and status.

Also ensure callers of `git_submodule_name_is_valid` correctly propagate negative error returns (e.g., from memory allocation during normalization) instead of swallowing them silently.

submodule: also validate Windows-separated paths for validity


The reflog module contains several defects and error-reporting inconsistencies that make it difficult to use and sometimes silently break operations:

1. **`git_reflog_rename` always reports `GIT_ENOMEM` and never performs the rename.**
   The condition that checks whether both path buffers were successfully constructed is logically inverted. Even when the source and destination paths are built without error, the function returns an out-of-memory error instead of invoking the rename, making it impossible to move a reflog through this API.

2. **Reflog message validation for embedded newlines happens too late and uses a generic error.**
   When a message contains a newline character, the library detects it only after it has already appended the old OID, new OID, and committer information into a temporary buffer. The failure returns a non-specific error code and does not set a clear `GITERR_INVALID` state, leaving callers with no actionable error information.

3. **Trailing newline handling causes double newlines in the log file.**
   If a message already ends with a newline, the library unconditionally appends its own `\n`, resulting in a blank line (an empty reflog entry line) that violates the on-disk format. The library should detect a trailing newline and avoid adding a second one.

4. **Memory leaks during reflog parsing when an entry or its signature fails to allocate or parse.**
   The `reflog_parse` function does not free an already-allocated `git_reflog_entry` and its committer signature if a subsequent allocation or parse step fails. This leaves memory unreachable when recovering from a malformed reflog file.

5. **Legacy error patterns do not populate the libgit2 error object.**
   Many reflog functions rely on old `git__throw`/`git__rethrow` macros that return error codes without calling `giterr_set`, meaning that `giterr_last()` cannot retrieve meaningful error messages (for example, when the old OID is missing for an existing reference, or when path construction fails).

**Expected behavior**
- `git_reflog_rename` should correctly rename the reflog file when both path buffers are valid, and return `0`.
- Message validation (including detection of embedded newlines) should happen before any log content is built, and raise `GITERR_INVALID` with a descriptive message.
- A message that already ends with `\n` should not receive a second newline; only messages without a trailing newline should be terminated with `\n`.
- All allocated resources (entry, committer, any intermediate strings) must be freed on any parsing failure inside `reflog_parse`.
- All error paths must set the thread-local error via `giterr_set` so that callers obtain both a return code and a human-readable description.


**Title:** Honor global `safe.directory` config to allowlisting untrusted repository paths

**Background**
When opening a repository, libgit2 enforces that the repository directory is owned by the current user. If the ownership check fails, the operation is denied with `GIT_EOWNER`. This is a safety measure, but it blocks legitimate shared-repository workflows.

Git itself already supports a `safe.directory` configuration mechanism that lets users mark directories as “safe” even when they are not owned by the calling user. We need the same escape hatch.

**Request**
Extend the repository-open ownership validation so that it consults the `safe.directory` configuration variable after the initial ownership check fails. Key behavior and constraints:

1. **Scope of configuration**
   Only the **global** and **system** configuration levels are consulted. The repository’s own local config must **not** be read for this variable; otherwise a malicious repository could mark itself safe and bypass the check entirely.

2. **Multivalued variable**
   `safe.directory` is a multivalued config entry. Each value is an absolute directory path. The validation must iterate over all values found in order.

3. **Matching logic**
   For each value:
   - Normalize the path (resolve `.`, `..`, trailing slashes, etc.) to a canonical absolute form suitable for the platform.
   - Compare the normalized path against the normalized absolute path of the repository being opened.
   - If they match, the repository is considered **safe**, and no error is returned.
   - An **empty value** (i.e., a blank entry) acts as a “reset”: it clears any previously accumulated safe-directory state. If a blank entry is encountered, any earlier matches in that config scope are discarded, and the repository is not safe unless re-added by a later entry.

4. **Fallback to original error**
   If, after processing all `safe.directory` entries the repository path has not been allowed, the original ownership error (`GIT_EOWNER`) is raised as before.

5. **No persistent state**
   The safe-directory evaluation is specific to each `git_repository_open*` call and does not leak state across operations.

**Background / Intent**
This matches the well-defined semantics of `safe.directory` from upstream Git. The blank-entry reset allows users and administrators to compose configuration includes while being able to override earlier allowlisting.

The implementation must be careful to use the same path normalization semantics as the existing ownership check (e.g., `git_fs_path_prettify_dir`) so that trailing slashes, redundant separators, or case differences do not cause mismatches.

repo: honor safe.directory during ownership checks


When applying a patch that contains a `\ No newline at end of file` marker (e.g., unified diffs for files missing a final newline), the output file incorrectly gains or loses a trailing newline. The parser currently strips the trailing newline from the preceding line’s content and does not emit an explicit EOF newline status. This makes `git_patch_from_buffer` / `git_apply` unable to reconstruct the intended file endings, and patches round-trip incorrectly.

**Observed Behavior**
- Parsing a patch with `\ No newline at end of file` truncates the last line’s `content_len` by one, removing its `\n`, and does not produce any `GIT_DIFF_LINE_*_EOFNL` line.
- Applying such a patch results in either a missing final `\n` when it should be present, or a spurious newline when the file should have none.
- The in-memory `git_diff_line` objects do not match the original diff intent.

**Expected Behavior**
- The parser must treat the `\`-prefixed EOF marker as an instruction to create a separate line with the appropriate `git_diff_line_t` origin:
  * `GIT_DIFF_LINE_CONTEXT_EOFNL` ( `'='` ) for context sections,
  * `GIT_DIFF_LINE_ADD_EOFNL` ( `'>'` ) for added lines,
  * `GIT_DIFF_LINE_DEL_EOFNL` ( `'<'` ) for deleted lines.
- The line immediately before the marker must remain intact (include its trailing `\n`).
- When applying a patch, the application logic must recognise these EOFNL lines and remove the trailing newline from the previous line’s content in the reconstructed preimage/postimage, ensuring the output file lacks a final newline exactly where the marker indicated.
- A patch that is parsed and then rendered back via `git_patch_to_buf` must retain the `\ No newline at end of file` markers in their original positions.

**Impact**
Many real-world patches contain files with no final newline, and currently libgit2 corrupts the line endings when applying them. This prevents correct use of `git_apply` and related APIs for these patches, and causes round-trip fidelity failures for patch formatting.

patch_parse: ensure valid patch output with EOFNL


When `git_apply` is used with `GIT_APPLY_LOCATION_BOTH`, apply must refuse to proceed if the working directory content for any file named in the patch’s preimage differs from that file’s content in the index.  The operation must fail with an `EAPPLYFAIL` error containing the message “does not match index” for the offending path, and must leave both the index and the working directory completely untouched.

This should mirror the safety guard in `git apply --index`: the index and the working tree must agree for the paths that would be changed before a patch is applied to both.  Previously, the library would attempt to apply to both locations even when the working directory was dirty relative to the index for the affected files, which could lead to checkout conflicts or an inconsistent application state.  Now, the mismatch must be detected early, during the preimage read, so that the user receives a clear, immediate failure before any modifications are made.

apply: validate workdir contents match index for BOTH


The `git_repository_head_tree()` API currently returns `0` and sets the output tree to `NULL` whenever it cannot resolve HEAD to a tree—for example, when the repository has no commits yet, when HEAD is an orphaned branch, or when the HEAD reference is missing entirely. This makes it impossible for callers to distinguish between a legitimate empty/success case and various error conditions, leading to silently incorrect behavior in higher-level operations like checkout and status.

**Problem:**
- Calling `git_repository_head_tree()` on a freshly initialized repository (no HEAD) or on a repository with an orphaned HEAD returns `0` with a `NULL` tree.
- Callers that need to know whether a tree was actually retrieved have no error indication. Workflows that could treat a missing HEAD as a valid empty state (e.g., starting a checkout or status with no base tree) currently have no way to do so without second-guessing the internal state of the repository.

**Expected behavior:**
- The function should return a distinct error code when HEAD cannot be resolved to a tree.
  - When the HEAD reference itself does not exist (e.g., brand new repo with no refs), it should return `GIT_ENOTFOUND`.
  - When HEAD points to a branch that has no commits (orphaned), it should return `GIT_EORPHANEDHEAD`.
- The output tree pointer should remain `NULL` in these error cases (the function must not leave a dangling pointer).
- Callers of this function (such as `git_checkout_head` and `git_status_foreach_ext`) should be updated to handle these specific error codes gracefully: they should treat `GIT_ENOTFOUND` and `GIT_EORPHANEDHEAD` as “no HEAD tree available,” and continue as if they had received a `NULL` tree with success, rather than propagating a fatal error. All other error codes should still be treated as failures.
- The fix must not alter the behavior for repositories with a valid, reachable HEAD—in those cases, a valid tree is returned with a success code.

**Impact:**
- Without this change, any program using `git_repository_head_tree()` and performing conditional logic based on its result cannot safely differentiate between a valid empty tree and an error condition.
- The silent `NULL`-tree return also violates the principle of least surprise, as the function otherwise follows a `return 0 on success` contract.

The fix should ensure that the error codes are consistently returned, and the known internal callers are adjusted to interpret the new error codes as non-fatal absent-HEAD cases.

repo: Make git_repository_head_tree() return error codes


The filesystem refdb backend’s loose-ref iterator does not scope per-worktree references correctly. When listing references in a worktree, the iterator walks the common directory and returns per-worktree refs (e.g. `refs/bisect/*`) that belong to the main repository. The worktree’s own per-worktree refs, stored in its private gitdir, are missing from the enumeration.

Fix the iterator so that:
- In a worktree, per-worktree refs found in the common directory are excluded from the result.
- In a worktree, per-worktree refs are collected from the worktree’s own gitdir and included in the result.
- In the main repository, per-worktree refs from its gitdir (which is the common directory) are still listed as today.
- Glob filtering and lock-file skipping apply to both passes.
- No per-worktree ref leaks across repository boundaries; each repository sees only its own per-worktree refs.

Make refdb_fs fully aware of per worktree refs


I’m calling `git_config_open_ondisk` to read a local config file, but when the file does not exist the function doesn’t return a clear “not found” error. In some cases it seems to succeed or return a generic error, making it impossible for the caller to reliably detect a missing config file and act accordingly (e.g., create a default file or skip loading).

**Expected behavior:**
- `git_config_open_ondisk` should return `GIT_ENOTFOUND` when the specified `path` does not exist (or is not a regular file), and set a descriptive error message.
- Similarly, `git_config_add_file_ondisk` should also return `GIT_ENOTFOUND` if the file doesn’t exist, since it’s adding an on-disk file that must already be present.

**Impact:**
Without a dedicated error code, lower-level library code and applications cannot distinguish a missing file from other failures. Updating these two APIs to signal `GIT_ENOTFOUND` for missing files would make error handling predictable and allow proper fallback logic. Internal callers that depend on opening a config file that may not yet exist (e.g., during repository initialization) would need to ensure the file is created before calling these functions, but that is an internal adjustment.

config: Opening a nonexistent file returns ENOTFOUND


### Feature Request: Enforce exclusive static mount prefixes and fix path rewriting for static files

**Problem**
- When using a `StaticFilesConfig` with a path like `/static`, it is currently possible to register HTTP route handlers under that same path prefix (e.g., `GET /static/foo`). This creates ambiguous routing and breaks static file serving. The framework should **prevent conflicting routes** by validating that no route handler is placed “below” a static files mount – and vice‑versa – raising `ImproperlyConfiguredException` whenever a conflict is detected.
- The existing path rewriting inside the ASGI router for static file requests uses `str.replace(static_path, '')`, which removes **every** occurrence of the mount path string from the request path. If the static directory hierarchy contains a subdirectory whose name matches the mount path, the resulting rewritten path is incorrect. For example, with a mount at `/static`, a request for `/static/static_assets/static/file.txt` would be mangled, leading to a 404 or incorrect file lookup.

**Desired Behavior**
1. **Validation:**
   - When a `StaticFilesConfig` is added, if any registered route (or any component that implies a static mount deeper down) exists under the same path prefix, raise `ImproperlyConfiguredException`.
   - When a new route is registered (via `app.register` or at startup), if it would fall under an already‑defined static mount prefix, likewise raise `ImproperlyConfiguredException`.

2. **Correct path stripping:**
   - Only strip the static mount path from the **beginning** of the request path, and only when the request path starts with that prefix.
   - Do not continue traversing deeper path components once a static mount node is reached; treat the mount as a terminal node.

**Acceptance Criteria (observable)**
- Calling `Starlite()` with both a `StaticFilesConfig(path="/static", ...)` and a handler registered directly at `/static/{param}` (or any sub‑path) raises `ImproperlyConfiguredException`.
- Registering a route under an existing static prefix *after* app creation (via `app.register()`) also raises `ImproperlyConfiguredException`.
- With a static mount at `/static` and a directory structure containing a nested folder also named `static` (e.g., `static/static_part/static/test.txt`), a GET request to `/static/static_part/static/test.txt` correctly returns the file contents.
- The rewritten `scope["path"]` used to locate the file inside the static directories is exactly the path relative to the mount root (e.g., `/static_part/static/test.txt`), not a globally‑replaced mangled version.


**Title:** OpenAPI schemas generated for dataclass and `msgspec.Struct` members ignore field default values

**Background**
When a dataclass or `msgspec.Struct` is used as a return type or request body, the generated OpenAPI schema’s `properties` should reflect whether a field has a default value. Currently, both the built-in `DataclassSchemaPlugin` and `StructSchemaPlugin` never pass a meaningful `default` when creating `FieldDefinition` objects, so every property appears as if it has no default even when the Python definition includes `field: str = "hello"`, `field(default=…)`, or `field(default_factory=…)`.

**Desired behavior**
- If a dataclass field has a static default (assigned directly or via `default=`), the OpenAPI property’s `default` must be set to that value.
- If a field uses `default_factory`, no static default can be derived; the property should either omit `default` or mark the field as required (preferably not produce a misleading `None`).
- For `msgspec.Struct`, the same distinction must be made: simple default values are represented in the schema; the sentinel values `NODEFAULT` and `UNSET` indicate absence of a default and should be treated accordingly.

**Acceptance criteria**
- A dataclass with `field_a: str = "default"` and `field_b: str = dataclasses.field(default="default")` both show `"default"` in the schema’s property `default`.
- A `msgspec.Struct` with `field_a: str = "default"` and `field_b: str = msgspec.field(default="default")` likewise both show the default.
- Fields defined with `default_factory` do **not** receive an incorrect static default in the schema.
- Fields without any default remain without a `default` in the schema (treated as required per OpenAPI semantics).

**Affected components**
- `litestar._openapi.schema_generation.plugins.dataclass.DataclassSchemaPlugin.to_openapi_schema`
- `litestar._openapi.schema_generation.plugins.struct.StructSchemaPlugin.to_openapi_schema`

The plugins should use the existing `Empty` sentinel from `litestar.types` to mark “no default” when constructing `FieldDefinition.from_kwarg`, ensuring the downstream schema generation respects that sentinel and correctly populates `Schema.default`.

fix(OpenAPI): Ensure default values are always represented in schema for dataclasses and `msgspec.Struct`s (#3285)


## Controller must reject duplicate route handler definitions for the same resolved path and HTTP method/type

**Context**
When a `Controller` subclass defines route handlers (HTTP, WebSocket, or ASGI), the framework composes each handler’s path from the controller’s `path` attribute and any handler-specific path. Currently, if two handlers end up serving the same HTTP method on the exact same resolved path, the application starts without an error and the routing behavior is ambiguous (e.g., last registered wins or unspecified). This can lead to subtle production bugs.

**Desired behavior**
Add startup validation to `Controller` so that, after collecting all route handlers, any conflict between two handlers that share both **the same resolved path** and an **overlapping method/type** immediately raises an `ImproperlyConfiguredException`. The check must treat:

- HTTP handlers according to their defined HTTP methods (e.g., `GET`, `POST`, etc.)
- WebSocket handlers as having the method set `{"websocket"}`
- ASGI handlers as having the method set `{"asgi"}`

Non-conflicting combinations must continue to be accepted. Examples:

- ✅ Two `@get()` handlers on the same path → **must raise**
- ✅ `@get()` + `@post()` on the same path → **accepted**
- ✅ `HTTPRouteHandler` (any method) + `WebsocketRouteHandler` on the same path → **accepted**
- ✅ Handlers with explicit differing sub-paths (e.g., `@get("/a")` vs `@get("/b")`) → **accepted**
- ✅ Handlers with identical sub-paths and overlapping method sets → **must raise**

**Edge cases / invariants**

- Validation must operate on the fully resolved paths (`route_handler.paths`) already available after `get_route_handlers` combines controller prefix and handler paths.
- The error message should include the controller class name, the conflicting path, and the overlapping methods/type names to ease debugging (e.g., *“… the following methods get for MyController controller path /ctrl are not unique”*).
- The check should run early, during route registration (e.g., as part of `Controller.get_route_handlers`), so that the application fails fast at start-up.

**Acceptance criteria**

1. When a controller defines two HTTP handlers with identical resolved paths and at least one shared HTTP method, an `ImproperlyConfiguredException` is raised before the application starts.
2. Different HTTP methods on the same path, and different handler types (HTTP vs WebSocket vs ASGI) on the same path, are allowed without error.
3. Handlers that only differ by explicit sub-path are treated independently and do not cause a conflict.
4. The exception is raised from a clear validation step, not from a routing ambiguity at request time.

implement validation of controller route handler methods (#1144)


The `LoggingMiddleware` fails with a `KeyError` on `HTTP_RESPONSE_START` when a response sends more than one `http.response.body` message (e.g., Server-Sent Events). The ASGI send wrapper currently pops the start message on the first body, causing the second body to fail. The middleware must handle multi-body responses without errors and ensure the logging context is cleared only after the final body (`more_body` is false).

fix: logging middleware with multi-body response (#3478)


We need to replace all `assert` statements used for runtime validation with explicit exception raises. Relying on `assert` is unsafe in production because Python’s optimizer (`-O` or `PYTHONOPTIMIZE`) strips assertions, silently turning hard errors into no-ops. This leads to undefined behavior or cryptic failures downstream. Instead, invalid states should always raise appropriate exceptions with the same descriptive messages.

Acceptance Criteria

1. **Invalid handler precondition (`self.fn` or `signature_model` missing)**
   - When `BaseRouteHandler.resolve_dependencies` is called before a signature model has been generated, the system raises a `RuntimeError` with the message `"resolve_dependencies cannot be called before a signature model has been generated"`.
   - When `BaseRouteHandler.validate_handler_function` is called without `self.fn` being set, the system raises an `AttributeError` with the message `"cannot call validate_handler_function without first setting self.fn"`.
   - When `HTTPRouteHandler.handle_request` or `WebsocketRouteHandler.handle_websocket` is called without a decorated function, the system raises an `AttributeError` with the message `"cannot call a route handler without a decorated function"`.

2. **WebSocket handler definition errors**
   - When a websocket handler function has a non‑`None` return annotation, an `ImproperlyConfiguredException` is raised with the message `"websocket handler functions should return 'None'"`.
   - When a websocket handler function does not include a `socket` keyword argument, an `ImproperlyConfiguredException` is raised with the message `"websocket handlers must set a 'socket' kwarg"`.

3. **Unsupported path parameter type in OpenAPI generation**
   - If `create_path_parameter_schema` encounters an unsupported path parameter type, a `TypeError` is raised with the message `f"Unsupported path param type {parameter_type}"`.

4. **Scope‑based authentication attributes missing**
   - Accessing `request.user` or `request.auth` (or the equivalent properties on `WebSocket`) when the corresponding key is not present in the scope raises an `ImproperlyConfiguredException` with the message `"user is not defined in scope, you should install an AuthMiddleware to set it"` (and the analogous message for `auth`).
   - The same applies to `websocket.user` and `websocket.auth`.

5. **Route path validation**
   - Creating any `BaseRoute` subclass with a path that does not start with `"/"` raises an `ImproperlyConfiguredException` with the message `"Routed paths must start with '/'"`.

6. **Existing test suites that previously expected `AssertionError` for the above conditions are updated to expect the corresponding concrete exception types (e.g., `ImproperlyConfiguredException`).**

No assertion that guards against an invalid program state (as opposed to an internal invariant that should never fail) should remain. All such assertions must be converted to explicit exception raises that cannot be disabled by optimization flags.

Remove asserts and replace them with respective exceptions


The `ServerSentEvent` response currently emits the `event_type`, `event_id`, `retry_duration`, and `comment_message` fields exactly once at the beginning of the stream. When the content is an iterable (sync or async) that yields multiple items, only the first SSE event carries those fields; all following data lines are concatenated into a single event payload without repeating the header fields. The request is to change the behavior so that every yielded item becomes its own complete SSE event, each one including the configured `event_type`, `event_id`, `retry_duration`, and `comment_message` (unless overridden for that specific item).

Specifically:

- For any `content` iterable, each item yielded must be sent as a separate SSE message (one empty-line-delimited block), not merged into a multi-line `data:` block.
- The server-sent fields set through `__init__` (`event_type`, `event_id`, `retry_duration`, `comment_message`) must appear in every SSE message, not just the first one.
- As an enhancement, allow content iterators to yield `dict` or an `EventMessage`-like object so that per-message fields can override the stream-level defaults. When a yielded item is a `dict`, treat its `data`, `event`, `id`, `retry`, `comment` keys as the fields for that message, falling back to the stream-level defaults. When it is an `EventMessage`-like object, use its attributes directly.
- Ensure that the stream still correctly obeys line-break sanitization and that an empty trailing line terminates each message.
- If no fields are specified for a message, emit only the `data:` line(s) as before.

fix: sse event type is only sent once (#2888)


The logging middleware obfuscates sensitive fields (like cookies) before logging them. Currently this obfuscation unintentionally mutates the original request and response data dictionaries, which can corrupt data shared with other middleware. In particular, using the logging middleware together with the session middleware causes the session cookie value to be permanently replaced with the obfuscation placeholder `"*****"`, breaking session management.

## Expected Behavior

1. **No side effects on original data**
   Obfuscation of cookies (or any field) during request/response logging must not modify the actual cookie dictionaries held by the ASGI connection or response. The original values remain intact for use by other middleware and handlers.

2. **Session cookie integrity**
   When both the logging middleware and the server‑side session middleware are active, the session cookie must retain its true value (a UUID) across requests, and must never be logged or stored as `"*****"` in place of the real session ID.

3. **Logging still masks sensitive data**
   The log output produced by the logging middleware should still show `"*****"` for any field marked for obfuscation, while the actual data structures used by the framework must be left unmodified.

## Acceptance Criteria

- A `POST` request that sets a session value responds with a session cookie containing a proper session ID, not the obfuscation string.
- A subsequent `GET` request that sends that same session cookie is able to use the session normally (cookie value unchanged).
- Logs emitted for the request/response cycle contain `"*****"` for obfuscated fields, without affecting the values seen by other parts of the system.
- The `obfuscate` utility used during extraction must not mutate its input dictionary; it must return a separate dictionary with the obfuscated values.

Fix #1228: create a new dictionary of values in obfuscate() (#1231)


We need to reduce per-request overhead by eliminating unnecessary runtime `isawaitable` checks when calling route handlers and dependency providers. The framework already inspects the function signature at registration time; it should also capture whether the function is a coroutine function. At invocation time, the decision to `await` the result must use that precomputed flag rather than inspecting the returned value.

**Acceptance Criteria**

1. When a route handler (HTTP, WebSocket, ASGI) or a dependency provider is registered, the system must detect whether the callable was defined with `async def` and store that boolean on the associated signature model.
2. When the handler function or dependency is invoked, the framework must use the stored async flag to decide whether to `await` the result. It must not call `isawaitable` on the returned value at invocation time.
3. A synchronous function (`def`) whose return value happens to be awaitable must **not** be awaited—the decision is based on the function’s declaration, not the result’s duck-typing. This keeps behavior predictable and avoids subtle timing differences.
4. All existing observable behaviors (status codes, response bodies, dependency caching, error handling) remain unchanged.

**Constraints / Hints**
- Use `inspect.iscoroutinefunction` (or equivalent) at model creation time to populate the flag.
- The flag should live on `SignatureModel` so it is accessible to both `BaseRouteHandler` and `Provide`.
- Remove the now-unnecessary `isawaitable` imports from `handlers.py` and `provide.py` once all usages are migrated.


As an API developer, I want to override specific generated OpenAPI schema fields for individual request parameters and bodies, so that I can emit schema constructs (like `anyOf`, `not`, custom `title`, etc.) that are not directly expressible through the existing `Parameter`/`Body` configuration options.

**Acceptance Criteria**

1. The factory functions `Parameter(...)` and `Body(...)` accept a new optional keyword argument `schema_extra` of type `dict[str, Any]` (or `None`).
2. When the OpenAPI schema for a parameter or request body is generated, the keys and values from `schema_extra` are applied as attribute overrides on the final `Schema` object **after** all other processing (constraints, examples, etc.) has completed.
3. The override is a direct attribute assignment; no recursive merging of nested structures is performed. The provided values must be exactly the correct types expected by the `Schema` dataclass attributes (e.g., `Schema` instances, lists of `Schema`, strings, integers, etc.).
4. If any key in `schema_extra` does not correspond to an existing attribute of the `Schema` class, schema generation must fail immediately with a clear error (e.g., a `ValueError` indicating the invalid key) at startup/OpenAPI build time, to avoid silent misconfiguration.
5. The behaviour is supported for parameters declared via `Annotated[..., Parameter(...)]` and for request body fields declared via `Annotated[..., Body(...)]`, including when they appear as route handler parameters or inside dataclass/typeddict models used as request bodies.
6. The existing behaviour for all other schema generation (including parameters and bodies without `schema_extra`) is unchanged.

These overrides are intended as an advanced escape hatch; they are not recommended for common use but enable users to emit OpenAPI constructs that Litestar does not yet have first-class API for.

feat: Support `schema_extra` in `Parameter` and `Body` (#3204)


Add a `configure_root_logger` flag to `LoggingConfig` (defaulting to `True`). When set to `False`, the root logger must be left exactly as it was before `configure()` is called — no handlers added, no level changed. The rest of the logging setup (formatters, handlers, and non-root loggers) should still be applied. This must work for both the standard `logging` module and `picologging`, and the default `True` path must behave identically to the existing code.

feat: allow `root` logger configuration to be disabled (#2969)


The framework should let developers customize the OpenAPI component name generated for a type annotation, so they can avoid collisions when the same Python type must appear as distinct schemas, or share a single component across multiple locations even if the framework would normally generate different keys.

Acceptance criteria:

1. A new parameter `schema_component_key` is accepted by `Parameter`, `Body`, and `KwargDefinition`. When set, any OpenAPI component generated for the corresponding type annotation uses that string as its component key (the name in `#/components/schemas/<key>`) instead of the auto-derived key.
2. If the same type annotation is used with different `schema_component_key` values in different places, each distinct key produces an independent component definition.
3. If the same type annotation is used with the same `schema_component_key` value in multiple places, they all reference the same OpenAPI component.
4. If two *different* type annotations attempt to use the same `schema_component_key`, or if a type with a custom key collides with an existing component registered for a different type, the framework raises a clear error during OpenAPI schema generation (e.g., `ImproperlyConfiguredException`), preventing silent overwrites.
5. The default key derivation (module path + qualified name) remains unchanged when no `schema_component_key` is provided.
6. The feature applies to types used in request bodies, parameters, and response definitions, regardless of whether they are decorated with `Parameter`, `Body`, or defined via `Annotated` metadata.

feat: Allow customizing schema component keys (#3738)


# Bug Report

**Title:** Handlers with `DTOData[Model]` parameter but no `dto` defined should be a configuration error

**Description**
When a route handler function declares a parameter of type `DTOData[SomeModel]`, the framework requires a DTO to be associated with the handler (either directly via the `dto` argument on the decorator, or inherited from a controller/router). Currently, if no DTO is provided, the application starts without raising an error. This leads to confusing failures at runtime when the DTO data cannot be deserialized, instead of failing early with a clear configuration error.

**Steps to Reproduce**
1. Define a dataclass model.
2. Create a route handler that accepts a parameter of type `DTOData[Model]` but does not set the `dto` argument on the route decorator (and no DTO is inherited from the controller or router).
3. Instantiate a `Litestar` app with this handler.
4. Optionally, attempt to call the endpoint.

**Expected Behavior**
The application should raise an `ImproperlyConfiguredException` immediately when the handler is registered, indicating that a DTO is required for a `DTOData` parameter but none was provided.

**Actual Behavior**
The application starts successfully. Subsequent requests to the endpoint may fail with obscure errors related to deserialization or DTO resolution, making the root cause hard to diagnose.

**Reproducibility**
Always reproducible in Litestar when using `DTOData` without a DTO.

**Impact**
This is a developer experience issue. Without an early check, users waste time debugging runtime errors that are actually simple configuration mistakes. The fix should catch the mismatch at startup and provide a clear error message similar to other configuration validations already performed in `BaseRouteHandler._validate_handler_function`.

**Suggested Fix**
Inside the handler validation logic (likely in `BaseRouteHandler._validate_handler_function`), after the handler function is set, check whether any parameter is typed as a subclass of `DTOData`. If such a parameter exists and `resolve_data_dto()` returns `None`, raise `ImproperlyConfiguredException` with a descriptive message naming the handler function and the problem. The check should be performed early enough to prevent the application from starting in an inconsistent state.

fix: DTOData annotation without DTO (#2789)


**Title:** CSRF middleware still sets cookies on excluded safe-method routes

**Area:** Middleware / CSRF
**Affected code:** `litestar.middleware.csrf.CSRFMiddleware` and `litestar.config.csrf.CSRFConfig`

### Summary
When a route is excluded from CSRF protection via `CSRFConfig.exclude` patterns or the `exclude_from_csrf` route opt key, the middleware should be completely skipped for that route. Currently, only the token validation step is bypassed, while safe-method requests (GET, HEAD, OPTIONS) to excluded routes still cause the middleware to set a CSRF cookie. This behaviour is inconsistent and pollutes client storage unnecessarily.

### Desired behaviour
- For any request whose scope matches a configured exclusion (either pattern or opt-out key), the CSRF middleware **must not** generate or set a CSRF cookie, regardless of the HTTP method.
- The request should be forwarded directly to the next ASGI layer without any middleware processing.
- Token validation, token generation, and cookie-setting must all be skipped.
- This applies to both safe and unsafe methods on excluded routes.

### Current behaviour
- The middleware checks `request.method in self.config.safe_methods or should_bypass_middleware(...)`.
- If either condition is true, it still generates a token (if the cookie is missing) and wraps the `send` callable to set the cookie.
- Consequently, visiting an excluded route with a GET request will still create and attach a CSRF cookie to the response.

### Acceptance criteria
- Requests to excluded routes do not receive a `Set-Cookie` header for the CSRF cookie.
- The middleware performs no work for excluded routes beyond the existing scope-type and websocket guards.
- Existing behaviour for non-excluded routes is unchanged.

### Notes
- The fix should place the bypass check early in `__call__`, before any token or cookie logic, analogous to how the middleware already returns early for non-HTTP scopes and websocket connections.
- The `should_bypass_middleware` helper already encapsulates both the pattern and opt-key logic; this can be reused without changing its contract.
- No new configuration options are needed.

fix: csrf middleware excluding router (#3698)


**Title:** Channels backends: trailing messages after unsubscribing
**Labels:** bug, channels
**Priority:** medium

### Problem
After calling `ChannelsBackend.unsubscribe(channel)`, a subsequent `await`ed `stream_events()` iteration can still yield a message for the unsubscribed channel.

This happens when a message was already published to the internal queue (or dispatched by the broker) before the unsubscribe call, but the stream loop has not yet processed it. The caller logically expects that once `unsubscribe()` returns, no further messages for that channel will appear.

The issue affects all backends that buffer messages in a local queue or have asynchronous processing of unsubscriptions. Specifically:

- `MemoryChannelsBackend` – if a message is already in the internal `_queue` when the channel is dropped from `_channels`
- `AsyncPgChannelsBackend` – if a `LISTEN` notification already placed a value in the `_queue` before the `UNLISTEN` was processed
- `RedisChannelsPubSubBackend` – because the Redis pubsub client queues `UNSUBSCRIBE` commands; a message may be received for a channel that is still registered but already marked as pending-unsubscribe

### Desired behavior
`stream_events()` must not yield messages for channels that the backend no longer considers subscribed at the moment of yielding.

- For the memory and asyncpg backends this means checking the in-memory subscription set (`_channels` / `_subscribed_channels`) before yielding.
- For the Redis pubsub backend this means filtering out channels that are in `pending_unsubscribe_channels` so that messages arriving during the unsubscribe window are dropped.

### Acceptance criteria
1. After `unsubscribe(["foo"])` returns and the stream is subsequently read, no message with channel `"foo"` is yielded.
2. Messages for still-subscribed channels continue to be delivered normally.
3. The fix must not introduce deadlocks, nor disrupt the normal `task_done()`/queue ack semantics (for memory/asyncpg).
4. The behaviour should be identical across all three affected backends.

### Implementation notes
- The stream loop should inspect the effective subscription state *after* dequeuing and before yielding.
- For Redis: use `self._pub_sub.channels.keys() - self._pub_sub.pending_unsubscribe_channels` as the set of effectively subscribed channels.
- The abstract contract in `ChannelsBackend` remains unchanged; the guarantee is strengthened at the implementation level.

fix(channels): Trailing messages after unsubscribes (#2894)


```markdown
**Title:** OpenAPI schema incorrectly marks nullable required fields as not required across all model types

**Description**

When a model field is typed as `Optional[T]` or `T | None` _without_ a default value, the generated OpenAPI schema incorrectly omits the field from the `required` array. This leads OpenAPI tools and client generators to treat the field as optional, even though the server requires it at runtime.

**Affected model types**

The issue is present for all supported model types:

- standard `@dataclass` classes
- `msgspec.Struct` subclasses
- `attrs` classes
- Pydantic models

**Minimal reproduction**

```python
from dataclasses import dataclass
from typing import Optional

@dataclass
class User:
    name: str
    age: Optional[int]       # no default → must be provided
    note: Optional[str] = None  # has default → optional
```

Current OpenAPI `required` array: `["name"]`
Expected `required` array: `["name", "age"]`

**Observed behaviour**

- `age` is missing from `required`, even though the server rejects requests that omit it.
- `note` is correctly omitted from `required` because it has a default value.

**Expected behaviour**

A field should appear in the schema’s `required` list **iff** it has no default value, regardless of whether the type is nullable (`None` is a valid input). This is the behaviour mandated by the OpenAPI 3.1 specification and is how Pydantic’s own `model_json_schema()` works.

**Root cause**

Litestar currently conflates _nullability_ (the type includes `None`) with _optionality_ (the field has a default value). The `is_required` logic uses the presence of `None` in the type as an indicator that the field is not required, which is incorrect.

**Impact**

- Generated OpenAPI schemas are non‑compliant with the specification.
- Downstream tools (e.g., Swagger UI, Type‑Safe client generators) show these fields as optional, leading to confusion and integration bugs.
- The bug is particularly severe because **runtime request validation already enforces these fields as required**, creating a discrepancy between the schema and the actual API contract.

**Desired fix**

- Update the `FieldDefinition.is_required` property (or the computation in each schema plugin) so that a field is considered required **solely** based on the absence of a default value.
- Ensure the fix applies uniformly to dataclasses, `msgspec.Struct`, `attrs`, and Pydantic models.
- The runtime validation behaviour must remain unchanged (it already works correctly).

This would make the OpenAPI output consistent with the actual API behaviour and with the OpenAPI 3.1.0 specification.
```

fix(openapi): correctly mark nullable required fields in schema (#4687)


Add a property setter for `Litestar.debug` so that changing the debug flag after initialization dynamically adjusts the logger level and the logging configuration.

Currently, the log level is only determined once during startup. Toggling `app.debug` later has no effect on the log output. The goal is to have the application’s logger level and the raw logging config stay in sync whenever `debug` is assigned.

Requirements:
- `Litestar.debug` should become a property backed by a private attribute.
- Setting `debug = True` must set `app.logger` (if not `None`) to `DEBUG` level and update `app.logging_config.loggers["litestar"]["level"]` to `"DEBUG"` (when `logging_config` is a `LoggingConfig`).
- Setting `debug = False` must set the logger to `INFO` level and the config entry to `"INFO"`.
- The `Logger` protocol (in `litestar.types.protocols`) must expose a `setLevel(level: int)` method so that the logger can be updated in a type-safe manner.
- The initialization order should be adjusted: the debug property should be set after the logger has been configured, not before, so that the setter can safely call `setLevel` on the already-configured logger. Any logic that previously forced `DEBUG` on the logger inside `__init__` when `debug` is true should be removed—the setter now handles that.

Update `Litestar.logger` levels when setting `Litestar.debug` (#1482)


The SQLAlchemy plugin’s model-to-Pydantic conversion mis-handles column default values. Callable defaults (e.g., `Column(DateTime, default=now)`) are invoked eagerly during model construction, producing a fixed runtime value instead of a proper callable. Non-scalar default values and evaluated expressions also cause invalid field defaults.

Only use the column’s `default.arg` as a Pydantic field default when its type is a simple scalar known to `pydantic_factories.ModelFactory.get_provider_map()` (e.g., `bool`, `int`, `str`). For callable, expression, or other unsupported defaults, skip the default entirely, leaving the field required.

fix sql alchemy plugin handling of default values


Add a `media_type` field to additional response specs so route handlers can declare non-JSON response content types for OpenAPI. Prevent silent overwriting when an additional response’s status code collides with a success or automatically generated error response, and raise a configuration error instead.

- The `responses` argument on HTTP route handlers (`get`, `post`, etc.) currently accepts `Dict[int, AdditionalResponse]` where each entry is forced to `application/json`. Provide a way to set the media type (e.g., `text/plain`, `image/png`) by exposing a `media_type` field that defaults to `application/json`.
- Rename the spec class from `AdditionalResponse` to `ResponseSpec` (or similar) and move it to `starlite.openapi`, since it is only relevant for OpenAPI schema generation.
- In `create_additional_responses`, honour the declared media type instead of hardcoding JSON.
- In `create_responses`, after collecting success and error responses, check every additional response’s status code. If a status code already exists in the combined set of responses, raise `ImproperlyConfiguredException` with a message indicating the conflict. This avoids silently overriding a structured response description or validation-error schema.


When building the parameter model for a route handler, a `TypeError` during dependency resolution (for example, a required dependency that is not provided) raises an `ImproperlyConfiguredException` but discards all context about **which** handler triggered the failure and **what** the underlying type error was. This makes troubleshooting very difficult because the exception contains no message tying it back to a specific endpoint or to the root cause.

### Desired behavior
- When `SignatureModelFactory.create_signature_model()` catches a `TypeError`, it should attach a descriptive message to the `ImproperlyConfiguredException`.
- The message must include the name of the handler function (e.g., `"my_handler"`) and the string representation of the original `TypeError`.
- The exception should still be raised as an `ImproperlyConfiguredException` and preserve the original exception chain.

### Acceptable criteria / example
If a handler named `get_items` has an unprovided dependency that causes a `TypeError("a type error")`, the resulting exception message should be:

`"Error creating signature model for 'get_items': 'a type error'"`

…instead of an empty or generic `ImproperlyConfiguredException`.

### Constraints
- The fix is limited to the location where the `TypeError` is caught inside `SignatureModelFactory.create_signature_model()`.
- No other exception types or code paths need to change.
- The existing behaviour of re-raising as `ImproperlyConfiguredException` is preserved; only the attached message is enhanced.

Adds detail to exception raised during signature model creation.


**Title:** Multiple validation errors produce identical error messages in `extra` response detail

**Description:**
When a single request triggers more than one validation failure (for example, multiple request fields or query parameters that fail different constraints), the `extra` list in the resulting `ValidationException` contains duplicate copies of the first error’s message instead of the correct, individual messages for each failure.

**Steps to reproduce:**
1. Define a handler with two query parameters that have different `Annotated` constraints:
   ```python
   from litestar import get
   from litestar.params import Parameter
   from typing_extensions import Annotated

   @get("/")
   def handler(
       a: Annotated[int, Parameter(gt=5)],
       b: Annotated[int, Parameter(lt=5)],
   ) -> None:
       pass
   ```
2. Send a request with `?a=0&b=9` (both values violate their constraints).
3. Observe the JSON error response body.

**Observed behavior:**
The `extra` array contains two entries, but both have the same `"message"` (e.g., `"Expected 'int' >= 6"`). The second entry is missing the expected `"Expected 'int' <= 4"` message.

**Expected behavior:**
Each entry in `extra` should contain the message specific to the validation failure of that individual parameter/field. In the example above, the `extra` array should contain one entry with `"Expected 'int' >= 6"` for `a` and a second entry with `"Expected 'int' <= 4"` for `b`.

**Impact:**
Client-side debugging is hindered because error messages for all invalid fields are wrong except possibly the first one. The problem also affects error tracking and can mislead developers during integration and testing.

**Root cause (guess):**
In the error collection loop inside `SignatureModel.parse_values_from_connection_kwargs`, the code appears to construct the message using the outermost exception (`e`) rather than the current iteration’s exception (`exc`). When a single `ValidationError` aggregates multiple sub-errors, `exc` holds each individual failure, but the message string is incorrectly derived from `str(e)` for all entries.

fix: multiple validation error messages (#2716)


We should update `SimpleEventEmitter` so that when an event is emitted, all registered listeners for that event ID are invoked concurrently rather than sequentially. Currently, listeners run one after another, which causes unnecessary latency when multiple slow listeners are attached to the same event.

## Acceptance Criteria

1. **Concurrent execution on `emit`**
   When `app.emit(event_id, ...)` is called, every listener registered for `event_id` must be started concurrently.
   - Sync listeners should run in a way that does not block other concurrent listener tasks.
   - Async listeners should run as separate tasks that do not await each other.

2. **Correct argument propagation**
   All positional and keyword arguments passed to `emit` must be forwarded correctly to every listener, regardless of execution order or concurrency.

3. **Reliable teardown**
   After the application context exits (or the test client context exits), all pending listener tasks must be fully completed before the event emitter is torn down. This ensures that tests can assert listener behaviour immediately after a request without relying on arbitrary `sleep` calls.

4. **Existing tests pass without time-based workarounds**
   The existing test suite (`tests/unit/test_events.py`) must continue to verify the same behaviour but should no longer need `time.sleep` delays to guarantee that mock assertions succeed. All listener invocations must be observed reliably by the time the test client scope ends.

5. **No regressions for basic event flow**
   Event emission, listener registration, and shutdown behaviour should remain unchanged in all other respects, and errors in listeners must not silently break the emitter’s shutdown process.

feat(events): run listeners concurrently (#2083)


When a handler body parameter is a model with a field annotated as `list[UploadFile]`, uploading a single file for that field should be treated as a list containing that one file. Currently validation fails because the extracted value is not a list.

Additionally, if an expected model field is entirely absent from the multipart form data, extraction should not raise a `KeyError`; let the validation layer report the missing field error instead.

Make multipart extraction ensure that for sequence-typed fields where the inner type is `UploadFile`, the value passed to validation is always a list (wrapping a solo value when necessary). Use the `FieldDefinition` utilities to inspect the type hierarchy and type hints rather than manual inspection of `inner_types`.

fix: handle single file in cases of multiple file uploads (#2950)


### Custom JWT Token Payload Classes

**Component:** `litestar.security.jwt`

#### Summary
The current JWT security backends (`JWTAuth`, `JWTCookieAuth`, `OAuth2PasswordBearerAuth`) always decode incoming tokens into the fixed `Token` dataclass. While the `extras` dict can hold additional claims, there is no built-in way to define a type‑safe, structured payload. Users should be able to subclass `Token` with their own fields and have the auth backends transparently decode into that subclass.

#### Requirements

1. **`token_cls` option on auth backends**
   - All JWT auth configuration classes (`BaseJWTAuth` and its concrete subclasses) should accept a `token_cls` parameter that specifies the token class to use for decoding. Default value: `Token`.
   - The middleware classes must be aware of this class and use it when decoding tokens.

2. **Decoding behavior**
   - `Token.decode` should be updated to accept a target class and return an instance of that class.
   - Standard claims (`exp`, `iat`) must be parsed as UTC datetimes.
   - Any claim in the JWT payload that does not match a defined field on the target class should be collected into the `extras` dict on the resulting instance, preserving backward compatibility for payloads with unknown claims.
   - Fields defined on the subclass should be populated with basic type coercion (e.g., string‑to‑int, string‑to‑bool). Complex type conversions (e.g., Pydantic models, custom decoders) are not required; users who need such behavior can override `encode`/`decode` on their subclass.
   - The base `Token` class should continue to work as before.

3. **Token creation with custom fields**
   - `BaseJWTAuth.create_token` should accept arbitrary keyword arguments that are forwarded to the token class constructor. This allows setting custom fields when creating a token from the backend.

4. **Documentation**
   - Document the new feature with an example of a subclassed token, and note the limitation regarding complex type conversions and the alternative of overriding `encode`/`decode`.

5. **Type safety**
   - The auth backends should be generic over the token type (`TokenT` bound to `Token`, defaulting to `Token`) to preserve type‑safety in middleware and request handlers.

feat(JWT): Custom JWT payload classes (#3692)


Make OpenAPI example generation deterministic by exposing a configurable random seed. When `create_examples` is enabled in `OpenAPIConfig`, the currently non‑deterministic random generation causes the schema to change on every rebuild, leading to spurious CI diffs.

Add a `random_seed` field (default `10`) to `OpenAPIConfig` and use it to seed the `ExampleFactory` before constructing the schema, so that identical seeds produce identical schemas. The seed should only be applied when `create_examples` is `True`; it must be forward‑compatible with any internal seeding mechanism used by the example factory.

feat: make random seed for openapi example generation configurable (#3166)


### Feature request: Support single-composite nested unions in DTO codegen backend

**Description**

When a DTO model field is typed as a union (e.g., `Union[int, list[Inner]]` or `Union[None, str, list[Inner]]`) where exactly one arm is a composite type (list, dict, tuple, etc.) and the remaining arms are simple scalars, the codegen backend should correctly transfer inbound data (builtins) into the corresponding domain model instance. This is common when a client may send either a scalar or a structured nested payload for the same field.

Currently, unions containing any composite types are either skipped or produce incorrect results, making it impossible to use such type annotations with the experimental codegen DTO backend.

**Expected behavior**

1. When a union contains **one** composite type and one or more simple types:
   - For scalar values that match a simple type (including `None`), the value is assigned directly.
   - For a composite value (e.g., a list of dicts), the backend recursively processes the nested data according to the composite’s type arguments (e.g., `list[Inner]` → each element is transferred as an `Inner`).
   - The generated code should handle the simple types first (using `if`/`elif` checks) and fall through to the composite branch via `else`.

2. When a union contains **multiple** composite types (e.g., `Union[list[str], dict[str, int]]`), the backend must raise a descriptive runtime error because automatic resolution of multiple composite branches is ambiguous and not supported.

3. The behavior must be consistent regardless of whether the backend is creating the final model instance (`is_data_field=True`) or converting an instance back to builtins.

4. `None` as a union member should be handled as a simple type; the generated code should explicitly check `is None` instead of relying on isinstance.

**Example**

```python
class Inner(msgspec.Struct):
    value: str

class Outer(msgspec.Struct):
    some_field: Union[int, list[Inner]]
```

Given a list of dicts `[{"value": "hello"}]`, the backend should produce an `Outer` whose `some_field` is `[Inner(value="hello")]`. Given the integer `1`, `some_field` should be `1`.

feat(DTO): Support nested union types with single composites (#4293)


The `create_function_signature_model` helper includes the `"return"` annotation from `getfullargspec` when building the pydantic model used to parse request parameters. This causes handlers with a return type annotation (e.g., `-> None`) to produce a model with a spurious `"return"` field, breaking request parsing for those handlers. Exclude the `"return"` key from the field definitions loop so the signature model only contains actual parameter annotations.


The exception handlers passed to the `Starlite` constructor are silently ignored and never applied to routes. When `Starlite.__init__` runs, the route map and its per-route exception-handling middleware are already constructed (via `register` → `construct_route_map`) before `self.exception_handlers` is set, so the middleware always sees an empty handlers dict. User-defined exception handlers must be available at the time the route middleware stack is built so that they actually intercept exceptions during request handling.

Fixes route exception handler resolution.


Make `Request.json()` safe to call more than once during a single request lifecycle.
Currently a middleware (or any earlier component) that consumes the body with `await request.json()` drains the underlying stream, causing later calls (e.g., in the route handler) to fail or return empty data.

Cache the raw request body on `request.scope` after the first read so that `Request.json()` returns the same parsed result every time, regardless of how often it is invoked before a response is sent.


Make `LifeSpanHandler` clean up its internal memory object streams (`stream_send`, `stream_receive`) after use so they are not leaked.

Turn `LifeSpanHandler` into a context manager: entering performs startup, exiting closes both streams.
Inside `AsyncTestClient.__aenter__` and `TestClient.__enter__`, enter the `LifeSpanHandler` on the exit stack so cleanup happens when the client context ends.

For existing code that creates a `LifeSpanHandler` without a context manager, keep implicit startup working but emit a `DeprecationWarning` (streams will not be automatically closed in that mode).

fix: LifespanHandler memory stream cleanup (#3836)


Feature request: Support `@property` attributes as read-only DTO fields for both `msgspec` and `dataclass` models.

## Problem
Today, Litestar’s DTO system ignores `@property` attributes when generating field definitions for `msgspec` `Struct` subclasses or standard `@dataclass` models. That means properties cannot appear in generated OpenAPI schemas and are never serialized in responses, even when they provide meaningful computed values with type annotations.

## Acceptance Criteria

1. **Automatic inclusion**
   When a model (`Struct` or `@dataclass`) defines a `@property` whose getter carries a return type annotation, `DataclassDTO` and `MsgspecDTO` must include it as a DTO field alongside the regular fields.

2. **Read-only semantics**
   The generated DTO field must be marked as `read‑only` (`mark="read-only"`). This ensures the property appears in response serialization and OpenAPI schemas but is **never** accepted during input deserialization.

3. **Private member exclusion**
   Any property whose name starts with an underscore (`_`) must be omitted from the DTO fields.

4. **Correct type resolution**
   The type of the DTO field must be the return type of the getter, resolved within the model’s own module namespace (so that forward‑references, local type aliases, and imported types work correctly).

5. **No extra configuration**
   This behaviour should function out of the box for both `DataclassDTO` and `MsgspecDTO`, without requiring users to add explicit annotations or change their existing models.

## Constraints / Edge cases
- The implementation must not disturb existing field definitions or override explicit DTO field marks.
- Properties without a getter or without a return type annotation may be skipped or treated as `Any`, but at least annotated read‑only properties must be supported.
- Must work in the presence of nested types, generics, and other advanced typing features already supported by the DTO system.

feat(DTOs): Support `@property` fields for msgspec and dataclass (#3981)


**Title:** `create_exception_response` leaks internal details in 500 responses

**Scope:** `litestar/middleware/exceptions/middleware.py` → `create_exception_response`

**Problem:**
When an unhandled exception triggers a 500 Internal Server Error via the exception middleware, the response body includes the exception’s `detail` attribute or its Python `repr` (e.g., `RuntimeError('yikes')`, `"Template invalid.html not found."`, or dependency validation messages). This leaks potentially sensitive implementation internals to clients.

**Expected behaviour:**
For any 500 response, the `detail` field must be a generic `"Internal Server Error"`, regardless of the original exception’s attributes. For non‑500 responses the existing behaviour (exception `detail` or `repr`) is acceptable.

**Implementation notes:**
- Modify `create_exception_response` so that when the resolved `status_code` is `HTTP_500_INTERNAL_SERVER_ERROR`, the `detail` is hard‑coded to `"Internal Server Error"`.
- Other response fields (`status_code`, `headers`, `extra`) should remain unchanged.
- This applies to all 500 responses produced by the middleware, including those from `HTTPException` with `status_code=500` as well as arbitrary non‑HTTP exceptions.

Fix: Internal Server Error detail (#1857)


**Title**: CLI options are silently ignored when environment variables are set

**Description**
When running a Litestar app via `litestar run`, CLI options (e.g., `--host`, `--port`, `--reload`, `--workers`, `--debug`, `--pdb`, SSL options, and the reload‑dir/include/exclude arguments) are ignored if the corresponding environment variable is already present. Instead of the CLI option taking precedence, the value from the environment variable is always used.

**Steps to reproduce**
1. Set `LITESTAR_HOST=0.0.0.0`.
2. Run `litestar run --host 127.0.0.1`.
3. Observe that the server still binds to `0.0.0.0`.

The same behaviour occurs for `LITESTAR_PORT`, `LITESTAR_RELOAD`, `LITESTAR_DEBUG`, `LITESTAR_PDB`, `WEB_CONCURRENCY`, `LITESTAR_UNIX_DOMAIN_SOCKET`, `LITESTAR_FILE_DESCRIPTOR`, `LITESTAR_SSL_CERT_PATH`, `LITESTAR_SSL_KEY_PATH`, `LITESTAR_CREATE_SELF_SIGNED_CERT`, and the reload‑dir/include/exclude options.

**Expected behaviour**
Explicit CLI arguments should **override** any environment variable. Environment variables should only be used as fallbacks when the corresponding CLI option is not provided. This is the standard convention for command‑line tools; the current behaviour is counter‑intuitive and prevents operators from temporarily overriding values that are baked into the environment.

**Additional context**
- For the reload‑dir/include/exclude options, environment variables like `LITESTAR_RELOAD_DIRS` use comma‑separated lists. If a user passes `--reload-dir` on the CLI, that should replace the env‑var list entirely, not merge with it.
- The precedence change should apply uniformly across all `run` subcommand options without breaking existing functionality when no CLI argument is supplied.

feat: Added precedence of CLI parameters over envs (#3190)


We're using the `crawlResource` API in `packages/core/src/typeschema/crawler.ts` to traverse FHIR resources in a sync fashion, but the only sync overload that accepts `options` (like `{ initialPath: ... }`) is not available; it's only exposed for the async variant via an `options` object. We need to crawl a resource synchronously with options like `initialPath` to limit traversal scope, but the current signatures force us to use the deprecated positional `schema`/`initialPath` overload, which doesn't support all the `ResourceCrawlerOptions` (e.g., `skipMissingProperties`). Furthermore, the async path is tightly coupled to a promise-returning overload of the same function name, which is confusing. Would it be possible to:

- Add a sync `crawlResource(resource, visitor, options?)` overload that takes a `ResourceVisitor` and an optional `ResourceCrawlerOptions` object, supporting at least `initialPath`, `skipMissingProperties`, and `schema`.
- Provide a standalone `crawlResourceAsync` function for async crawling, so callers don’t need to rely on the overloaded `crawlResource` returning a promise (which is harder to discover and type-check).
- Deprecate the old positional overloads (`crawlResource(resource, visitor, schema?, initialPath?)` and the async `crawlResource` overload) to guide users toward the new signatures.

Backward compatibility should be preserved: existing code that calls `crawlResource` with the old signatures must continue to work without changes. The async behavior should remain identical to what `new AsyncResourceCrawler(...).crawl()` does today. This is blocking us from adding a sync traversal with options in our data processing pipelines.

Update function signatures for crawlResource (#5060)


**Title:** Azure storage `writeFile` crashes on string data (`TypeError: this.readable.on is not a function`)

**Description:**
The Azure Blob Storage adapter (`AzureBlobStorage.writeFile`) only calls `uploadStream()` on the input. When the caller passes a string value (which is a valid `BinarySource`), the Azure SDK tries to treat the string as a readable stream and throws:
`TypeError: this.readable.on is not a function`

This happens, for example, when a bot or a system component writes a `Binary` resource whose content is a plain string (e.g., a JSON payload). The AWS S3 adapter already handles strings correctly, so this divergence breaks code that expects the `BinarySource` type to be storage-agnostic.

**Expected behavior:**
`writeFile` must accept both Readable streams and plain strings. For strings, it should upload them as blob content using the appropriate SDK method that accepts a string/buffer directly (not via `uploadStream`). The existing stream path should remain unchanged.

**Acceptance criteria:**
- Uploading a string to Azure Blob Storage succeeds without errors.
- Stream uploads continue to work as before.
- The selection between the two paths is based on the runtime type of the `BinarySource` argument (do not rely on a new parameter).

Update azure storage to handle string data in writeFile method (#7206)


**Bug:** Deleting a Bot that runs on AWS Lambda (runtimeVersion `awslambda`) leaves the underlying Lambda function undeleted, causing orphaned cloud resources.

**Expected behavior:**
When a Bot resource is deleted, the dispatch worker should detect that the Bot was deployed on `awslambda` and issue a deletion of the associated Lambda function using the standard naming convention (`medplum-bot-lambda-{botId}`).

**Details and constraints:**

- The Lambda cleanup should only happen for Bot resources whose `runtimeVersion` is `'awslambda'`, and only when the interaction is `'delete'`.
- If the Lambda does not exist (for example, it was already removed manually), the system must treat that as a normal condition: do **not** log an error, and do **not** fail the dispatch job.
- For any other failure during Lambda deletion, the error should be recorded (via the existing structured logger) but the dispatch job must **not** abort—the Bot resource deletion itself should still be considered successful.
- The Lambda function name must be derived using the canonical helper that already exists for creating Lambda functions for bots (`getLambdaNameForBot`). The Lambda deletion should use the existing `LambdaClient` infrastructure, specifically the client obtained via `getBotManagementLambdaClient`.
- This cleanup is part of the background dispatch job processing, not a synchronous side-effect of the FHIR delete operation.

feat(server): delete Lambda when a Bot is deleted (#9259)


When creating or updating resources via the GraphQL `$graphql` endpoint (`create` or `update` mutations), providing a resource that contains a nested object (for example, an `Identifier`) can cause the mutation to fail when the authenticated user's access policy includes search criteria that cause FHIRPath evaluation to be triggered on that resource (e.g., `Task?group-identifier=...`). The reported error references a missing `valueOf` method.

The root cause is that the GraphQL argument parsing layer produces objects with a null prototype (`Object.create(null)`). When these objects reach access control checks that evaluate FHIRPath expressions against the resource, the evaluation fails because FHIRPath expects normal object prototype methods (such as `valueOf`) to be present.

**Request:**
Ensure that the resource payload passed from GraphQL mutation resolvers to the repository's `createResource` / `updateResource` methods is converted to a standard object tree (with the normal object prototype chain) before the repository processes it. This should apply to **both** create and update GraphQL mutation paths. No changes to the REST endpoints are needed; the problem is specific to the GraphQL argument parsing. The behavior after the fix should be that a GraphQL mutation with nested objects succeeds under access policies that evaluate FHIRPath criteria that reference those nested fields.

fix(graphql): clone resource before create in mutations (#5706)


**Title:** Wildcard `*` access policy entries are silently dropped when intersected with SMART scopes, and read‑only scope merging is missing

**Impact:**
When a user configures an access policy that includes a wildcard resource entry (`resourceType: '*'`) and then applies SMART scopes via `applySmartScopes`, all coverage for resource types not explicitly listed is lost. This prevents deployment patterns that rely on a base wildcard policy being narrowed by scopes during OAuth/OpenID flows.

**Observed behaviour**
- `applySmartScopes(accessPolicy, scopes)` where `accessPolicy` contains `{ resourceType: '*' }` does **not** expand the wildcard to the concrete resource types present in `scopes`.  The resulting policy contains only entries for resource types that were already listed explicitly – the wildcard entry disappears.
- When a scope is read‑only (e.g., `patient/Observation.rs`, which contains only `r` and `s`), the corresponding policy entry is **not** marked `readonly: true`.
- Invalid scope suffixes that don’t match the expected `c`, `r`, `u`, `d`, `s` combination are accepted without error (e.g., `patient/Observation.chum` parses successfully).

**Expected behaviour**
1. **Wildcard expansion.** Any `{ resourceType: '*' }` entry in the policy should be replaced by one entry per resource type found in the SMART scopes, each inheriting the base policy’s permissions (e.g., parameter constraints) but restricted to that concrete resource type.
2. **Read‑only merging.** For each resource type where the SMART scope consists only of read/search letters (`r` and `s`, without `c`, `u`, or `d`), the resulting access policy entry must have `readonly: true`. If the original explicit entry was already read‑only and the scope is not read‑only, the entry should preserve its original `readonly` value (i.e., a broad scope should not accidentally strip an existing read‑only restriction).
3. **Stricter scope parsing.** Scope suffixes that contain characters outside the set `cruds` or that use `*` mixed with other characters (e.g., `*c`, `c*`) should be treated as invalid and ignored, so that they don’t produce unpredictable behaviour. A bare `*` suffix (e.g., `patient/Observation.*`) is valid and must be interpreted as `cruds`.

**Context**
- The intersection logic is invoked from `applySmartScopes` when a SMART scope string is passed alongside an `AccessPolicy`.
- An `AccessPolicy` resource entry looks like `{ resourceType: string, readonly?: boolean, … }`.
- SMART scopes follow the pattern `permissionType/ResourceType.suffix` where suffix is a string of `c`, `r`, `u`, `d`, `s` (or `*` for full access).

**Why this matters**
Without this fix, any authorisation flow that combines a wildcard default policy with SMART scopes will produce policies that are either too restrictive (missing resource types) or misconfigured (missing read‑only flag), leading to access denied errors for valid requests or unintentional write access in read‑only sessions.

Merge wildcard AccessPolicy and readonly scopes (#5700)


When the server processes FHIR resource writes (create/update), several server-side touchups are applied to the resource before storage—most notably resolution of conditional references and assignment of default profiles. Validation must be ordered to see the resource after those touchups.

**Acceptance criteria**
- Structural validation (e.g., field types, cardinality, required elements) runs *after* all write-path touchups, not on the raw submitted resource.
- Reference resolution (including conditional reference lookup) still executes, and its failures are surfaced, but they must not mask unrelated structural problems in the final resource shape.
- A resource that contains a valid-looking conditional reference plus an unrelated schema violation (such as an array where a single value is expected) must receive the schema violation error, not an error about the conditional reference format.
- Errors for unresolvable conditional references should be clear (e.g., “did not match any resources”) and produced by the resolution step, not misinterpreted as structural validation failures.
- Default profiles are applied before validation so that profile conformance checks see the final meta.
- The change must not weaken any existing validation; it only shifts the timing so that validation inspects the resource in its final pre-commit state.

Validate resources after touchup modifications (#5349)


**Title:** Resolve non-Medplum SMART Health Links by fetching and decrypting external payloads

Currently the `$resolve-smart-health-link` operation only processes links that point back to an existing Medplum `SmartHealthLink` resource.  Any other valid SMART Health Link (for example a direct link issued by an external system) is rejected with the error *“Only Medplum-generated SMART Health Links are supported by this prototype”*.  This blocks interoperability with standard SHL issuers, which is an important use case.

We need to extend the resolver so that when a link does **not** match a locally managed resource it performs the standard direct-link flow:

1. **Payload parsing**
   - The `v` field in a SHL payload is optional per the spec.  Our parser in `@medplum/core` currently requires `v === 1`; make it accept a missing `v` but still reject when `v` is present and not equal to 1.
   - The parser should continue to reject payloads that are missing `url` or `key`.

2. **Direct-only support**
   - Look at the `flag` field.  Only proceed when it contains `'U'` (per the SHL spec this signals a direct link with a JWE payload).  Reject anything else with a clear error message.

3. **Fetching the encrypted payload**
   - Use the `url` from the SHL payload to fetch the JWE from the issuer.
   - The `recipient` input parameter **must** be appended as a `recipient` query parameter.  Reject the request if `recipient` is missing.
   - Prevent SSRF by running the URL through our existing `validateOutboundUrl` utility.
   - Enforce a fetch timeout of **5 seconds** and a **maximum response size of 10 MB**.  Reject responses that exceed the size limit (check `content-length` header when available).
   - Follow redirects **must** be disallowed (treat a redirect as an error).

4. **Decryption and content validation**
   - Decrypt the JWE with the `key` from the payload using `compactDecrypt`.
   - Support the `"zip": "DEF"` protected header: if present, decompress the plaintext with raw deflate (`inflateRawSync`).
   - Only accept the content type `application/fhir+json`.  Reject any other content type (or missing content type) with an error.

5. **Expiration handling**
   - If the payload contains an `exp` timestamp that is <= now, **do not block resolution**.  Still decrypt and return the data, but include a **warning** indicating the link is expired.

6. **Operation output**
   - Add a new output parameter `warning` (type `string`, cardinality `0..*`) to the operation definition.
   - Return any non-error warnings (e.g., the expiration warning) through this parameter.
   - The existing error handling should stay unchanged: catch errors, set `valid = false`, and return the error message.

7. **Existing behaviour**
   - When the link *does* match a local `SmartHealthLink` resource, the current generated-link resolution path must continue to work exactly as before (including its own passcode checks, manifest, and `patientEverything` flow).  This change only affects links that cannot be resolved locally.

**Out of scope**
- The endpoint handlers `smartHealthLinkManifestHandler` and `smartHealthLinkPayloadHandler` are not directly involved, but you may need to adjust local imports to align with their current Express type usage (`ExpressRequest`/`ExpressResponse`) if you touch those files.

Support more SMART Health Link formats (#9660)


**Async batch (respond-async) uses the system repository instead of the user’s repository, bypassing access policies**

Currently, when a FHIR batch request is submitted with `Prefer: respond-async`, the async
job is created and later polled using the **system** repository (the unrestricted internal
repo). This means:

- The async job creation is not subject to the requesting user’s access policy.
- The job status endpoint reads the `AsyncJob` through the system repo, allowing
  any authenticated caller to read the job regardless of their project membership.

**Desired behavior**

Route all `AsyncJob` operations for a user‑initiated async batch through the **user‑scoped
repository** (the `repo` from the authenticated context, the same repo used by normal
synchronous batch processing). Concretely:

1. When handling a `Prefer: respond-async` batch, create and initialise the
   `AsyncJobExecutor` with the **user’s** repository, not the system repo. The
   system‑repo bypass is unnecessary because the caller is already authenticated and
   should be subject to normal access rules.
2. The job status endpoint (`/fhir/R4/job/:id/status`) must use the user’s repository to
   read the `AsyncJob`, not the system repo. The existing comment about exempting from
   rate limits still applies, but the read should respect the caller’s project and access
   policy.

**Non‑obvious constraints**

- The `AsyncJob` resource may require **extended mode** (the `X-Medplum: extended`
  header) because project‑scoped access policies typically do not grant read access to
  `AsyncJob` by default. Therefore the polling logic (and any test helpers like
  `waitForAsyncJob`) that call the status endpoint on behalf of a user **must** include
  `X-Medplum: extended` in the request, otherwise the read will be denied.
- The batch handler previously passed an explicit `meta: { project, author }` when
  initialising the executor. With the user repo, those values are already captured in
  the repo context and must not be duplicated or overridden.
- The `async-batch` handler no longer needs to destructure `profile` from the context;
  that information is now held by the repo.

Route AsyncJob operations through user repo (#9126)


```
Title: Show “on behalf of” information in History, Timeline, and Blame views

Description:
When a resource’s `meta` includes an `onBehalfOf` reference (indicating the author acted for another practitioner or organization), the UI should display that relationship in three surfaces.

Acceptance criteria:

1) ResourceHistoryTable (History tab)
   - The table currently has columns: Author, Date, Version.
   - Add a new column “On Behalf Of” between Author and Date.
   - If a history entry has `meta.onBehalfOf`, render the linked name/badge in this cell.
   - If the entry does not have the field, leave the cell empty.
   - The error row (shown when history fetch fails) must span the updated total number of columns so the layout remains correct.

2) ResourceBlame (Blame tab)
   - The blame view already shows the author in a cell.
   - When a version’s `meta` contains `onBehalfOf`, display “on behalf of <linked name>” directly below the author, using the same dimmed styling pattern as other secondary text in that component.
   - Do not render anything extra when the field is absent.

3) Timeline
   - Timeline items show the author as the primary actor.
   - After the author name, if `meta.onBehalfOf` is present, append a dimmed inline text: “ on behalf of <linked name>”.
   - If absent, no extra text appears.

General behavior:
- The `onBehalfOf` value is a reference (e.g., `{reference: "Practitioner/123"}`). Use the same linked name/badge components already used for the author, but style it dimmed to indicate a secondary role.
- “on behalf of” should be localised text (plain English for now).
- All existing functionality (rendering when the field is missing, author display, navigation, etc.) must remain unchanged.
```

Display onBehalfOf in History, Timeline, and Blame (#9206)


**Title:** Allow per-channel filtering of application-level HL7 ACKs forwarded by the agent in enhanced mode

**Description:**
When an HL7 channel is running in enhanced mode (`enhanced=true`), the Medplum agent forwards every application-level acknowledgment (ACK) it receives from bots back to the remote system. This can cause unnecessary traffic or duplicate ACKs, particularly for integrations where the remote system does not want all acknowledgment types (for example, only error/reject ACKs or only success ACKs).

The HL7 standard (MSH-16) defines four application acknowledgment modes: AL (always), NE (never), ER (error/reject only), and SU (success only). We would like the agent to respect one of these modes per channel, so that only the desired application-level ACK codes (AA, AE, AR) are forwarded.

**Expected Behavior:**
- The channel endpoint URL should accept a new optional query parameter `appLevelAck` with one of the four values: `AL`, `NE`, `ER`, `SU` (case-insensitive).
- When the channel operates in **enhanced mode**, the agent must use this setting to decide whether to forward an application-level ACK to the remote system:
  - `AL` (the default when the parameter is absent or invalid): forward all application-level ACKs.
  - `NE`: forward no application-level ACKs.
  - `ER`: forward only AE and AR (error/reject), not AA.
  - `SU`: forward only AA (success), not AE or AR.
- When the channel operates in **original mode** (`enhanced=false` or absent), the agent must continue to **forward all application-level ACKs regardless of the `appLevelAck` setting**, to preserve protocol compatibility.
- If the parameter is present but its value is not one of the allowed modes, the agent should log a warning and fall back to `AL` (always). The channel must continue to function.
- The introduction of this feature must not change the default behavior for any existing configuration: the agent should continue to forward all ACKs by default.

**Impact / Use Case:**
Integrations that use enhanced-mode HL7 often only need selective application acknowledgement feedback, reducing noise and socket traffic. This also aligns agent behavior with standard HL7 application acknowledgment expectations.

**Acceptance Criteria:**
- Query parameter `appLevelAck` is read from the endpoint address and applied by the HL7 channel.
- The ACK filtering is active only in enhanced mode; original mode is unaffected.
- Invalid parameter values are handled gracefully with a logged warning and fallback to `AL`.
- All four modes behave as described above for the standard ACK codes AA, AE, AR.

feat(agent): add channel config for app-level ACK (#7843)


The patient page in the Medplum Provider example app currently uses a custom `PatientTabsNavigation` component for tab navigation. For consistency and reuse, it should use the shared `LinkTabs` component from `@medplum/react`. However, `LinkTabs` cannot correctly handle tab values that include query strings.

In the patient page, some tabs have values like `Encounter?_count=20&patient=%patient.id`. When `LinkTabs` tries to determine the active tab by comparing the last path segment of the URL (e.g., `Encounter`) against the tab’s whole value (`Encounter?_count=20`), the match fails. This means the active tab isn’t highlighted, and navigation doesn’t preserve the query string.

We need `LinkTabs` to correctly detect the active tab when tab values contain a query string. The matching should use only the path part (ignoring the query string) and be case‑insensitive. When a tab is clicked, the full original tab value (including the query string) must be used for navigation.

After fixing `LinkTabs`, please replace the custom `PatientTabsNavigation` in the provider’s `PatientPage` with `LinkTabs`. The page should build tab entries from the existing `getPatientPageTabs` function, substituting the patient ID placeholder (`%patient.id`) in URLs to produce the values for `LinkTabs`. Remove the now‑unnecessary `PatientTabsNavigation` component and its CSS module.

**Expected behavior:**
- Navigating to `/Patient/abc/Encounter` highlights the tab with value `Encounter?_count=20&patient=abc`.
- Clicking that tab navigates to `/Patient/abc/Encounter?_count=20&patient=abc`.
- The old custom tab navigation is no longer used, and the page relies on `LinkTabs` for all tab rendering and behavior.

chore(provider): use LinkTabs in provider patient page (#9122)


**Title:** OAuth `/oauth2/authorize` crashes on missing or unparseable client redirect URI

**Description:**
When a registered `ClientApplication` resource has no `redirectUri` field, or the field is a string that cannot be parsed as a valid absolute URL (e.g. `"invalid"`), the OAuth authorize endpoint (`GET /oauth2/authorize` and `POST`) can throw an unhandled exception and return an HTTP 500 Internal Server Error. The endpoint must instead validate the stored redirect URI early and return a proper 400 Bad Request, before any logic that depends on a usable redirect URI (such as comparing it to the request’s `redirect_uri` or redirecting on error).

**Acceptance Criteria**
1. If the `ClientApplication` has no `redirectUri` (the field is missing/undefined), the authorize endpoint must respond with HTTP 400 and body `Client has no redirect URI`.
2. If the `ClientApplication.redirectUri` is present but is not a valid absolute URL (i.e., it cannot be parsed as a URL), the endpoint must respond with HTTP 400 and body `Invalid redirect URI`.
3. If the stored redirect URI is valid but does not match the `redirect_uri` query parameter, the endpoint must still respond with HTTP 400 and body `Incorrect redirect_uri` (existing behaviour).
4. None of these cases should cause an internal server error (500); the error responses must be delivered cleanly as 400-level OAuth error responses.

Gracefully handle invalide client redirect URI in authorize (#5780)


The `$db-configure-indexes` operation currently deadlocks against concurrent traffic when it disables GIN fastupdate. It calls `gin_clean_pending_list()` directly on each index, which enters index maintenance without following PostgreSQL's normal lock ordering, causing deadlocks instead of safe waiting.

Replace that cleanup step with a table-level `VACUUM` when fastupdate is set to `false`. `VACUUM` acquires locks in the proper hierarchy, so deadlocks will not occur. The `VACUUM` must run per table, after the index configuration changes have been applied.

Also, the operation's output Parameters format must change to reflect the new approach. Instead of per-index result parts with `schemaName`, `tableName`, `indexName`, and `pagesCleaned`, return action parts. Each action part contains the SQL statement that was executed (`sql`, string) and, for `VACUUM` actions, a `durationMs` (integer) recording how long the statement took. The admin UI that displays these results should simply show the full raw response rather than filtering for `result` parts.

Constraints:
- VACUUM only when the operation is disabling fastupdate (not during a "reset" or when setting other options).
- Use proper identifier quoting/escaping for table names in the VACUUM statement.
- Do not change the behavior when fastupdate is being enabled or reset; those paths should still just emit their ALTER INDEX statements.

Replace gin_clean_pending_list() with VACUUM (#7410)


```
Fix the `aws update-server` and `aws update-app` CLI subcommands so that users can specify a target version for the update.  Currently both commands accept a `--version` flag, but Commander's built‑in version flag intercepts it, making the flag ineffective – the version value never reaches the command handler.  Rename the flag to something that does not conflict with the CLI’s own `--version` (for example `--to‑version`), and ensure the parsed option is properly forwarded to the version‑selection logic in both commands.
```

Fix CLI update-server version flag (#4534)


**Bug: body‑parser errors (entity.too.large / parameters.too.many) return HTTP 400 instead of 413**

When the Express body parser rejects a request because the payload exceeds the size limit (error.type `"entity.too.large"`) or contains too many parts / parameters (`"parameters.too.many"`), the server currently responds with **400 Bad Request** and an OperationOutcome containing a generic message like `"File too large"`.

According to HTTP semantics, an oversized request entity should produce a **413 Content Too Large** response.  The response body must still be a valid FHIR OperationOutcome.

**Expected behavior**
- Requests that fail with `error.type === "entity.too.large"` or `error.type === "parameters.too.many"` receive HTTP status **413**.
- The accompanying OperationOutcome is updated:
  - The operation outcome uses an appropriate error code (`too-long`).
  - The description clearly indicates that the *request body* is too large (e.g., `"Request body too large"`).
- The central status‑mapping logic (from operation outcome to HTTP status code) is extended to return 413 for this outcome type, so any consumer of that mapping sees the correct status.

Transform body parser error to HTTP 413 (#9173)


**Title:** Return HTTP 504 for database statement timeouts instead of 500

**Problem**
When a database query exceeds the statement timeout, PostgreSQL cancels the query with error code `57014` (`query_canceled`). Currently the server treats this as an unhandled database error and returns a generic HTTP 500 response to API clients. This makes it harder for clients (and monitoring) to distinguish between a server crash and a transient timeout.

**Desired behavior**
When a database statement timeout occurs, the API should return HTTP **504 Gateway Timeout** with a proper FHIR `OperationOutcome` that indicates a timeout. This should work for all FHIR endpoints that rely on the database error-normalization pipeline.

**Acceptance criteria**
1. A new `serverTimeout()` helper is added in `@medplum/core`’s outcomes module, similar to the existing `serverError()` helper. It must produce an `OperationOutcome` whose `id` is `"server-timeout"` and whose issue `code` is `"timeout"`.
2. The `getStatus()` function in the same outcomes module must map the `"server-timeout"` outcome ID to HTTP status `504`.
3. The `normalizeDatabaseError()` function in `packages/server/src/fhir/sql.ts` (or equivalent database-error handler) is extended to catch Postgres error code `57014` and convert it to an `OperationOutcomeError` using the new `serverTimeout()` helper, analogous to how `23505` (unique violation) and `40001` (serialization failure) are already mapped to 409 Conflict.
4. The timeout error message from the database (typically the statement timeout message) should be included as the detail text of the outcome.
5. All other unhandled database errors must continue to be logged and returned as a generic error (currently 500 or 400 based on issue code). The new handling must not change behavior for other error codes.

**Edge cases / constraints**
- The fix must be compatible with the existing patterns in `@medplum/core` outcomes: a specific constant ID, an exported helper, and a case in `getStatus`.
- If the error object does not have a `code` property, the handler should fall through to the existing generic path (no crash).
- The existing logging of database errors should be preserved; a structured log entry with `error.message`, `stack`, and `code` can be added but is not required.
- Do not change the HTTP status code for any other existing outcome type (200, 201, 202, 304, 400, 401, 403, 404, 409, 410, 412, 429, 500). Treat `504` as a new addition.