In `PatternToken.PosToken`, POS tag matching currently uses `java.util.regex.Pattern` when the POS pattern is a regular expression. Elsewhere, regex patterns on token forms are already handled by the project's `StringMatcher`, which extracts required substrings to enable faster matching. `PosToken` should use `StringMatcher` as well, so POS regexes benefit from the same optimization and consistency.

Additionally, the required-substring extraction logic in `StringMatcher`’s `RegexpParser` does not handle regexes containing the `.` metacharacter well: patterns such as `PRP.+`, `.*PRP`, or `. + PRP` lose their literal parts entirely, returning no required substrings. Adjust the substring extraction so that literal segments surrounding `.` (and `.+`, `.*`, etc.) are still recognized as required substrings (e.g., `PRP` in `PRP.+`), without altering matching semantics. The fix must preserve correct extraction for all existing patterns and not introduce regressions when the dot is combined with quantifiers or appears at boundaries.

use StringMatcher for POS patterns as well


As a maintainer, you'd open a ticket like this:

**Title:** UppercaseSentenceStart false positive for mixed-case tokens starting with lowercase
**Labels:** bug, de, core-rule
**Priority:** medium

#### Problem
The `UPPERCASE_SENTENCE_START` rule (in `org.languagetool.rules.UppercaseSentenceStartRule`) incorrectly reports an error for German sentences such as:

    mRNA-Impfstoffe sind wichtig.

The word *mRNA-Impfstoffe* is a valid mixed-case term beginning with a lowercase letter. The rule currently treats any sentence-start token whose first character is lowercase as an error, unless it is listed in the `EXCEPTIONS` set or recognized as CamelCase by `StringTools.isCamelCase`. Terms like `mRNA`, `eBay`, or `iPhone` are not covered by those checks, so they are flagged.

#### Expected behavior
Tokens that start with a lowercase letter should still be considered legitimate sentence starts when the token contains an uppercase letter **immediately after the first character**, i.e. a lowercase-uppercase pattern at the very beginning of the token. This matches common mixed-case technical terms, brand names, and biological abbreviations.

The rule should **skip** such tokens (no error), while continuing to flag truly all-lowercase words (e.g. *aber*). All existing logic for CamelCase tokens, explicit exceptions, and digits must be preserved.

#### Affected languages
Primarily reported for German, but the rule is language-agnostic in the core module. The fix should apply generically.

#### Acceptance criteria
- `mRNA-Impfstoffe sind wichtig.` → 0 errors
- `eBay ist ein Unternehmen.` → 0 errors
- `aber das sollte ein Fehler sein.` → still reports error
- The change must not break any existing CamelCase, exception, or digit handling.

[de] fix for UPPERCASE_SENTENCE_START rule


**Request**: Improve French spelling suggestion quality for two cases:
- All-uppercase words (e.g., `"AAAAAAAAAAAH"`) should not produce unwanted single‑character split suggestions.
- When the French speller promotes a suggestion that splits the misspelling into a recognised start token and a remainder, the promotion should be suppressed if the remainder is only a single character (e.g., for `"Den"` move `"De"` to the top instead of `"De, n"`).


**Issue: Catalan text with decomposed combining characters causes incorrect error highlights and token offsets**

When checking Catalan text that contains decomposed Unicode characters (e.g., a base letter followed by a combining accent like `a` + U+0300 COMBINING GRAVE ACCENT), the spell‑checking underlines are drawn at wrong positions. Words that should be recognised are either flagged incorrectly or the error span (`fromPos` / `toPos`) covers a different number of characters than the actual input.

**Root cause**
The Catalan tokenizer currently applies NFC normalization to the entire input. That transforms decomposed sequences into precomposed characters early, altering character counts and making offset tracking relative to the original text impossible. Downstream consumers (tagger, spell‑check rules) receive already‑normalised token strings and compute positions that do not match the user’s input.

**Expected behaviour**
- The tokenizer should preserve the original Unicode representation of each token exactly as it appears in the input, so that character offsets remain correct.
- The tagger should perform NFC normalisation *internally* for its dictionary lookups and morphological analysis, while emitting `AnalyzedToken` objects whose surface form is the original token text (with any combining characters intact) and whose position offsets are based on that original form.
- The Catalan‑specific “additional tags” logic (adverb in `-ment`, prefixed verbs) must also normalise the forms it consults in dictionaries.
- A word like `"demana\u0300ren"` (10 characters, combining grave on the `a`) should be recognised as a valid form (e.g., verb) and produce spell‑check matches – if any – with `fromPos=0`, `toPos=10`, exactly matching the input length.

**Impact**
Without this fix, any Catalan text editor or proof‑reading tool that submits decomposed Unicode input will show highlights shifted or cover incomplete words, making the feature unreliable for users whose text contains such characters (common in keyboard input methods or copy‑paste scenarios).

[ca] normalize in the tagger, not the tokenizer


**Title:** `fixMatchOffsets` throws `ArrayIndexOutOfBoundsException` when match `toPos` equals sentence length

**Description:**
When a remote rule returns a match that ends exactly at the end of the sentence (i.e., `toPos == sentenceText.length()`), the offset correction step in `RemoteRule.fixMatchOffsets` fails with an `ArrayIndexOutOfBoundsException`. This happens because the internal shift lookup table (`computeOffsetShifts`) that maps Java string indices to Unicode code point offsets does not include an entry for the position at the very end of the string.

The expected behavior is that all valid match positions, including those equal to the length of the sentence text, should be supported without an exception. The fix must ensure that the shift array is sized and populated so that an index of `s.length()` is a valid lookup.

The scenario can be reproduced by constructing a `RuleMatch` with `toPos` equal to the length of the sentence text and passing it to `fixMatchOffsets`. A correct implementation will process such matches successfully, just as it does for matches that end within the text.

The change should be minimal and not affect the handling of existing match positions that fall strictly within the string. The lookup must still correctly map each Java `char` index to the corresponding code point offset for all positions from 0 to `s.length()` inclusive.

fix off-by-one error in computeOffsetShifts


**Title:** German agreement suggestions for "zur" produce wrong/missing contractions and mishandle preceding prepositions

**Description**
When the German agreement suggestion logic proposes corrections for phrases that include the contraction "zur" (meaning "zu der"), the generated suggestion lists are incomplete or incorrect. This happens when the underlying rule needs to suggest a corrected article and adjective combination that should contract with "zu".

**Observed behavior**
1. For feminine singular nouns, the suggestion may incorrectly output "zum" (masculine/neuter) instead of the expected "zur".
   *Example:* "gehe zur kuschelige Ferienwohnung" → the list is missing "zur kuscheligen Ferienwohnung" and may instead contain a form with "zum".
2. When a separate preposition like "bis" precedes "zur" (e.g., "bis zur …"), the case of the following noun is inappropriately restricted by that preposition, even though "zur" already determines the case. This leads to missing correct case forms or entirely absent suggestions.

**Expected behavior**
- For replacement suggestions based on "zur", the logic should correctly select the contracted form according to gender and number:
  * "zum" for masculine and neuter singular nouns
  * "zur" for feminine singular nouns
  * "zu den" for plural nouns
- A preposition that is not part of the "zur" contraction should **not** influence case generation, because "zur" itself implies dative case and the preposition-selection logic must not override that.

**Impact**
Users receive incomplete or inaccurate correction proposals for common German constructions involving "zur". This reduces the effectiveness of the proofreading tool and forces manual intervention.


When converting a `RuleMatch` to the protobuf `Match` message via `GRPCUtils.toGRPC`, the `url` field is only taken from `RuleMatch.getUrl()`.
Many rules define their URL through `Rule.setUrl()`, and the `RuleMatch` typically inherits it, but `RuleMatch.getUrl()` may return `null` if no explicit URL is set on the match itself. In that case the protobuf message ends up with an empty URL, even though the underlying rule has a valid URL.

This causes downstream consumers of the protobuf representation (e.g., clients using the ML/gRPC server interface) to miss the rule’s documentation or help link altogether.

**Expected behavior:**
- When creating the protobuf representation, if `RuleMatch.getUrl()` is not null, use it.
- Otherwise, fall back to the URL from the associated `Rule` (`RuleMatch.getRule().getUrl()`), if present.
- If neither is set, default to an empty string.
- If both define URLs, the `RuleMatch`’s URL must take precedence (it is the more specific override).


```
Title: WordRepeatBeginningRule false positives for emoji, arrows, and lowercase math variables

## Problem
The `WordRepeatBeginningRule` (repetition of word at the beginning of consecutive sentences) reports an error when multiple sentences start with the same emoji, arrow symbol, or lower‑case mathematical variable. For example:

- ➡ Ein Satz. ... ➡ Noch ein Satz.
- 👪 Ein Satz. ... 👪 Noch ein Satz.
- x = 2. ... x = 5. ... x = 6.

All three cases currently trigger a false positive: “Repetition of word at beginning of sentence”.

## Expected behavior
The rule should only flag repeated sentence beginnings when the first token of each sentence is an actual *capitalised word* (i.e., starts with an uppercase letter). Emojis, arrows, and lower‑case identifiers are not words and should be ignored by this rule. No error should be reported for the examples above.

## Impact
The false positives generate noise in documents that use emojis as bullet points, step markers, or that include lower‑case mathematical variables at the start of sentences. This reduces user trust in the check.
```

[core] improve WordRepeatBeginningRule to not match math equations and emojies


**improve suggestions for split words (merge adjacent tokens)**

The spell checker currently suggests splitting a single misspelled token into two correct words (e.g., “thankyou” → “thank you”). It does **not** suggest merging two adjacent tokens that should be one word (e.g., “g oing” should be “going”; “go ing” should be “going”).

Add merge suggestions so that when a misspelled token appears next to another token and combining them yields a correctly spelled word, the checker offers that merged word as a replacement.

Requirements:
- The suggestion span should cover both original tokens.
- The merged form should only be offered when it is correctly spelled **and** its frequency is higher than the frequency of the adjacent token alone.
- Both a split suggestion and a merge suggestion may be applicable for the same input; in that case both should appear as alternatives (e.g., “benv inguts” could suggest “ben vinguts” **and** “benvinguts”).
- The change must preserve the existing split-word behavior (e.g., “thankyou” → “thank you”, “unah ora” → “una hora”) and must not interfere with the detection of normal misspellings.

The feature should be active in both the Hunspell-based and Morfologik-based spelling rules.

improve suggestions for split words: g oing (#1729)


Title: Dutch tagger should assign noun POS to valid compounds using CompoundAcceptor

### Problem
Many correct Dutch compound words (e.g. `oorlogsbeker`, `beoordelingsgeschiedenis`) are left untagged (`null`) by `DutchTagger` because they are not in the static dictionary. This degrades rule suggestions that depend on POS information for these tokens.

### Background
The `CompoundAcceptor` (in `rules/nl/CompoundAcceptor.java`) already contains logic to decide whether a word is a valid Dutch compound (e.g. it enforces the linking `‑s‑`/`‑en‑` etc.). However, it is currently only used by some spelling‑check rules and is not consulted by the tagger.

### Request
Extend `DutchTagger.tag()` so that when a token is **not** found in the regular dictionary (i.e. the readings list is empty), it consults `CompoundAcceptor`. If the word is accepted as a compound, the tagger should attempt to split the word and assign a noun POS tag derived from the head (rightmost) component.

#### Behavioural requirements
- **Only for unknown words**: Already‑known words (e.g. `havermout`) must **not** be re‑tagged through this new code path.
- **Compound validation**: Use `CompoundAcceptor.acceptCompound(word)`; if it returns `false`, the token must remain untagged (as today).
- **Head tagging**: Split the word at every position that leaves at least 3 characters on each side. For each right‑hand part (`part2`), fetch its dictionary POS tags. If any tag contains `ZNW` (noun), try to verify that the left‑hand part (`part1`) is also a noun.
- **Left‑part check**: The left‑hand part must itself be a singular noun (`ZNW:EKV`) according to the dictionary, optionally after stripping a trailing `‑s` (common linking morpheme). Only if both sides are nouns should the reading be added.
- **Use the head’s full POS tag**: The assigned POS tag should be exactly the tag of the head noun (preserving number, gender, diminutive markers etc.), so that rules see the same tag they would see if the head appeared in isolation.
- **No other changes to the tagging loop**: The existing accent‑handling and token‑normalisation logic must remain untouched. The new block should be inserted only when `l.isEmpty()` (i.e. no dictionary readings).

#### Visibility
`CompoundAcceptor`’s constructor and `acceptCompound()` method currently have package‑private visibility. They must be made `public` so that `DutchTagger` (which lives in a different package) can use them. Ensure `acceptCompound` is declared to throw `IOException` if needed (the tagger already does).


The RepeatedWords rule has several misbehaviors related to proper nouns and sentence boundaries. The following acceptance criteria describe the expected improvements:

1. **Proper nouns after a period**
   In English, the pair of sentences
   `"This is a new experience. Happy New Year!"`
   should **not** trigger a repeated‑words warning. Currently the rule may incorrectly flag `New` as a repetition of `new` from the previous sentence.

2. **Multi‑word proper nouns following abbreviation**
   `"Asia Global Crossing Ltd. Global Crossing and Asia Global Crossing."`
   should **not** be reported as repeated words. The repeated occurrences of `Global Crossing` are a proper name of a company and should be treated as an exception.

3. **Repeated lemma detection after a sentence‑start exception**
   In Spanish, the text
   `"Esto propuse. ¿Propones tu algo diferente?"`
   should produce a match for `Propones` as a repetition of `propuse`, offering synonym suggestions such as `"Sugieres"` and `"Recomiendas"`. At present the rule may skip `Propones` entirely because it appears as a capitalized word after a punctuation‑marked sentence boundary, missing a genuine stylistic repetition.

The overall goal is to make the repeated‑words rule more reliable when the repeated items sit at the start of a new sentence, when they are part of a proper noun, or when they immediately follow a token that is itself a sentence‑start exception (e.g., a capitalized proper noun or a leading punctuation). The fix should handle both false positives (erroneous flags on proper nouns) and false negatives (undetected repetitions across sentences).


We need a way to detect renames from rewrites without changing the status output of those rewrites to “deleted” + “untracked”. When `GIT_STATUS_OPT_RENAMES_FROM_REWRITES` is used, heavily rewritten files should still appear as **modified** in the status list, while still being eligible to contribute to rename/copy detection.

### Acceptance criteria

1. **Diff‑find flag for “break only when useful”**
   Introduce a new flag for `git_diff_find_similar` (e.g., `GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY`) that alters the semantics of `GIT_DIFF_FIND_AND_BREAK_REWRITES`.
   - When this flag is set, a rewrite that exceeds the break‑rewrite threshold is **not** split into separate `ADD`/`DELETE` deltas *unless* the rewrite actually results in a rename or copy pairing.
   - If no rename/copy is detected for a split rewrite, the delta remains `MODIFIED`.

2. **Status always shows rewrites as modified**
   When `GIT_STATUS_OPT_RENAMES_FROM_REWRITES` is passed to `git_status_list_new`, the underlying diff‑find options must include the new flag.
   - After rename detection, any rewritten files that did **not** become part of a rename/copy must appear as `GIT_STATUS_WT_MODIFIED` (or `GIT_STATUS_INDEX_MODIFIED`), **not** as a deleted + untracked pair.
   - This matches core Git’s behaviour: `git status` never shows a rewrite as an add/delete pair.

3. **Internal splitting for detection remains active**
   Even when the “break only for renames” flag is on, the similarity engine must still split rewrites internally so that similarity scores can be calculated and rename/copy detection can proceed. The flag only affects which final deltas are exposed in the diff list returned to the caller.

4. **Compatibility with explicit break‑rewrite usage**
   If a caller uses `GIT_DIFF_FIND_AND_BREAK_REWRITES` **without** the new flag (e.g., for a direct diff), the current behaviour is preserved: all rewrites above the threshold are split into add/delete pairs regardless of rename/copy discovery.

Split rewrites, status doesn't return rewrites


Please fix the config backend so that included configuration values are refreshed correctly on every file change, not only on the first refresh after the parent file changes.

Currently, when a configuration file includes another file via `[include]path=…`, the first modification to the included file after loading the parent is picked up. However, further modifications are ignored – the config continues to serve the first-updated values until the parent itself is touched or the backend is reopened.

This happens because the internal list of parsed include entries is cleared **after** re-parsing the new configuration, rather than before. The old, stale includes survive into the parsing step and cause the updated content to be discarded.

Change the refresh logic so that before re-reading the configuration (both when refreshing from a buffer and when refreshing from disk), any previously stored include data is cleared. This ensures that each refresh operation starts with a clean slate for includes and fully re-imports the latest content.

config: fix included configs not refreshed more than once


We need two robustness fixes in libgit2:

1. **Self-cleaning test home directory mocking**
   When test code overrides the `GLOBAL` search path to simulate a home directory (e.g., to test global gitignore or config files), the original path must be restored automatically at the end of the test – even if the test fails mid‑way. Currently, a test crash or early exit leaves the override in place, contaminating subsequent tests with a fake directory.
   - The setup function for the fake home must register a cleanup handler that restores the saved search path.
   - Explicit cleanup must remain callable (with a `NULL` argument) so tests can restore and then re‑set the override within the same test.
   - Multiple or redundant cleanup calls must be harmless.

2. **Valgrind‑clean search‑path separator scanning**
   Libgit2 parses search‑path lists (e.g., the list of directories for system/global/XDG config files) where entries are separated by `GIT_PATH_LIST_SEPARATOR` and individual separators can be escaped with a backslash. Valgrind reports that the scanning logic can perform invalid, out‑of‑bounds memory reads when it reaches the end of the list.
   - The loop that advances past escaped separators must **never** read before the start of the string or beyond the terminating null.
   - It must correctly treat the null terminator as the end of the last entry without calling `strlen` (which would re‑scan).
   - The same safe scanning must be applied in all places that walk this list (e.g., `git_config__global_location` and `git_sysdir_find_in_dirlist`), and both functions must gracefully handle a `NULL` or empty string without crashing.


**Fix checkout when its hands are tied**

When `git_checkout_index` is called with a very restricted strategy mask (e.g. only `GIT_CHECKOUT_CREATE_MISSING`) and the working tree contains a typechange/rename conflict – specifically a directory where a blob should be placed, or vice versa – checkout currently fails with an error instead of completing successfully. Since no overwrite or removal strategy has been requested, checkout should silently skip these conflicting entries, just as it skips other modified files when not told to overwrite.

Additionally, the progress callback (`progress_cb`) should report an accurate total step count that reflects only the actions checkout actually intends to perform, not just the total number of diff deltas.

As a result, checkout must be able to handle a situation where a path that is a directory in the working tree conflicts with a blob that needs to be created (or the reverse), and it must finish without error when the strategy does not permit altering the existing working tree item.

Fix checkout behavior when its hands are tied


**Bug: Renaming a reference fails to move its reflog when names collide on a shared directory prefix**

**Observed behavior**
When I rename a reference that has a reflog, the reflog file is not relocated to the new path if the old and new reference names share a common directory prefix. For example:

- Renaming `feature/foo` → `feature/foo/bar` leaves the reflog at `logs/refs/heads/feature/foo` and no reflog at `logs/refs/heads/feature/foo/bar`.
- Renaming `a/b/c/d` → `a/b/c` leaves the reflog at `logs/refs/heads/a/b/c/d` and no reflog at `logs/refs/heads/a/b/c`.

In these cases the reflog move essentially fails; the old reflog file stays behind and the renamed reference ends up without any reflog history. No clear error is surfaced to the caller.

**Expected behavior**
When `git_reference_rename` is called on a reference that has a reflog, the reflog file must be moved to the new location, regardless of namespace overlap. The implementation should:

- Create any intermediate directories needed for the new reflog path.
- Handle the case where the target path already exists as an **empty directory** (because the old reflog resided inside that directory); the empty directory must be removed before the final rename can succeed.
- Ensure that the reflog is never left orphaned at the old path while the new reference points elsewhere.

**Impact**
Programs that rely on reflogs to track branch history will silently lose reflog entries when a branch is renamed into or out of a nested namespace. This makes it impossible to correctly implement operations like `git branch -m` on such references without a workaround that manually copies the reflog file. The repository state becomes inconsistent because the old reflog file persists under a name that no longer corresponds to any existing reference.

refs: fix moving of the reflog when renaming a ref


**Title:** CRLF filter turns existing CRLF into CRCRLF when `core.autocrlf=true`

**Background**
When `core.autocrlf` is set to `true` on Windows, libgit2’s checkout path applies a CRLF filter to the working‑directory representation of text files. The implementation in `src/crlf.c` uses `convert_line_endings` to replace line endings with the platform’s native ending (`\r\n`).

**Bug**
If a file already contains CRLF line endings (e.g., mixed LF/CRLF files, or files that have been stored with CRLF in the repository), the conversion incorrectly produces `\r\r\n` (CR‑CR‑LF) for those lines. Lines that are bare LF are correctly expanded to CRLF, but any line whose original ending is `\r\n` gains an extra carriage return, resulting in corruption.

The root cause is that `convert_line_endings` does not detect an existing `\r` immediately before the `\n` it locates; it unconditionally copies all content up to the `\n` and then appends the desired line ending, so the preceding `\r` becomes part of the copied content and a second `\r` is added.

**Expected behaviour**
1. Files checked out with `core.autocrlf=true` must preserve lines that are already CRLF (`\r\n`) without adding an extra CR.
2. Lines that are bare LF (`\n`) must be expanded to CRLF (`\r\n`).
3. No other bytes in the file should be altered, and the filter must correctly handle mixed line endings within a single file (e.g., some lines CRLF, some LF).
4. The output buffer must not grow by more than the necessary conversion (i.e., we should not insert an extra byte when the original line already had the CR).

**Acceptance criteria**
- A repository file containing `crlf\r\nlf\ncrlf\r\n` is checked out to a working directory with `core.autocrlf=true`; the resulting file on disk contains exactly `crlf\r\nlf\r\ncrlf\r\n` (no `\r\r\n` sequences).
- Files with only LF endings still become CRLF, and files with only CRLF endings remain unchanged.
- The fix must not introduce regressions for the existing “all‑LF → CRLF” conversion already covered in the filter.

**Affected code**
- `convert_line_endings` in `src/crlf.c` (used exclusively by `crlf_apply_to_workdir`).
- The fix should inspect the byte immediately before the `\n` located by `memchr`; if it is `\r`, the region copied before appending the new line ending must exclude that `\r`. The loop must then skip over the original `\r\n` or `\n` as appropriate.


When opening a repository using the path of a working directory (a directory that contains a `.git` subdirectory), the ref names reported by `git_reference_listall()` must never begin with a forward slash (`/`).  A leading slash is not a valid component of a Git reference name, and it causes problems for any code that expects clean ref names.

Additionally, the repository path returned by `git_repository_path()` and the working directory path returned by `git_repository_workdir()` (when the repository is not bare) must consistently end with a single forward slash after any successful open operation, regardless of whether the repository was opened via a direct path to the git directory, a working directory path, or a bare repository path.  This trailing slash guarantees that concatenating the path with a relative subpath (for example, `"refs/heads/master"`) produces a well-formed absolute path without a missing separator.

Acceptance criteria:
- `git_reference_listall()` never returns a string with a leading `/` for a repository opened through its working directory.
- For the same repository, `git_repository_path()` and `git_repository_workdir()` (if present) each end with `'/'` or the platform’s equivalent path separator.
- No existing callers that rely on the trailing slash convention break due to duplicated separators.
- Opening a bare repository continues to work correctly, and its `git_repository_path()` also ends with a trailing slash.

repository: ensure that the path to the .git directory ends with a forward slash when opening a repository through a working directory path


**Title:** `git_diff_patch` yields empty diffs for renamed/copied files after similarity detection

**Description**
When diffing two trees with rename/copy detection enabled, the patch output for entries with status `GIT_DELTA_RENAMED` or `GIT_DELTA_COPIED` does not contain any textual diff hunks. Only the file header (e.g., “diff --git a/old b/new”) is emitted; the actual line-level additions and deletions are missing. Entries with status `GIT_DELTA_MODIFIED` correctly include the content changes.

**Steps to reproduce**
1. Create two trees (commits) where a file was renamed (or copied) *and* its content was modified.
2. Diff the trees using `git_diff_tree_to_tree` with `GIT_DIFF_INCLUDE_UNMODIFIED` (to get the “old” side for copies) and the desired context configuration.
3. Call `git_diff_find_similar` with flags `GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES`.
4. Iterate over the deltas; for any delta whose status is `GIT_DELTA_RENAMED` or `GIT_DELTA_COPIED`, obtain the patch via `git_diff_get_patch` and convert it to a string using `git_diff_patch_to_str` (or print it with `git_diff_patch_print`).

**Observed behavior**
The resulting patch string for renamed/copied files contains the rename/copy *header* but no diff hunks. For example:

```
diff --git a/old_name.txt b/new_name.txt
similarity index 95%
copy from old_name.txt
copy to new_name.txt
```

There is no `@@ ... @@` hunk showing what lines were added or removed.

**Expected behavior**
The patch should include the full textual diff between the old and new file content, identical to what `git diff --find-renames` produces in the command line. A correct output would look like:

```
diff --git a/old_name.txt b/new_name.txt
similarity index 95%
copy from old_name.txt
copy to new_name.txt
index abc123..def456 100644
--- a/old_name.txt
+++ b/new_name.txt
@@ -1,3 +1,4 @@
 context line
-added line
 another context line
```

**Impact**
This prevents applications from obtaining or displaying the actual content changes when a file is moved or copied, making the diff API unusable with rename/copy detection. Users must currently work around it by manually reconstructing diffs from the original modified deltas, which defeats the purpose of the similarity analysis.

**Additional context**
The problem affects both `GIT_DELTA_RENAMED` and `GIT_DELTA_COPIED`. Modified deltas (`GIT_DELTA_MODIFIED`) are handled correctly. The issue appears to be in the internal patch-loading logic that decides whether to read file content; it does not account for the rename/copy states, treating them as if no data is needed.


**Title**: SSH credential callback receives all auth types, regardless of server support

**Observed behavior**
When connecting to an SSH remote, the credential callback (`git_cred_acquire_cb`) is invoked with `allowed_types` set to the full set of credentials the transport can handle:
`GIT_CREDTYPE_USERPASS_PLAINTEXT | GIT_CREDTYPE_SSH_KEY | GIT_CREDTYPE_SSH_CUSTOM | GIT_CREDTYPE_SSH_INTERACTIVE`.
This happens even if the remote server only supports a subset of those methods (e.g., only publickey). The credential helper has no way to know which methods the server actually accepts, so it must offer credentials that may never succeed.

**Expected behavior**
Before the credential callback is called, the SSH transport should query the server’s advertised authentication methods (using `libssh2_userauth_list`, as permitted by the SSH protocol). The resulting set of supported methods should be passed as `allowed_types` to the credential callback. This allows credential providers to filter and offer only relevant credentials.

**Edge‑case / fallback requirements**
- If querying the server for supported methods fails (connection error, timeout, protocol mismatch, etc.), the transport must gracefully fall back to the original full set, so the credential callback still has a chance to proceed.
- The method‑detection query should be performed on a dedicated, short‑lived SSH connection and session, not on the main session that will later be used for authentication. This avoids interfering with the authentication state.
- The query may use a well‑known dummy username (e.g., `"git"`) since the actual username is not available until the callback returns.

**Impact**
Applications and credential helpers receive accurate information about which authenticators the server supports. This prevents the UI from prompting for, or attempting, credentials the server will reject, eliminating confusing error paths and improving the user experience for SSH remotes with restricted authentication methods.


When updating a ref through the filesystem backend, if the new target (oid for direct refs, symbolic target for symbolic refs) is identical to the current one, the operation should be a no-op that does not append an entry to the reflog and does not rewrite the loose ref file. Use the existing old-value comparison to detect this case, and return success without touching the reflog.

refdb: don't update when there's no need


**Title:** Clone should not persist a default `tagopt` configuration setting

**Background**
When cloning, libgit2 currently writes `remote.<name>.tagopt = --tags` into the repository’s configuration by default. This forces future fetches to always download all tags, even though the natural default for a remote is auto‑follow (download tags that point to objects already being fetched).

In core Git, `git clone` does not write any `remote.<name>.tagopt` setting unless the user explicitly passes `--no-tags`. The initial clone downloads tags anyway, but the remote’s configuration remains absent/auto, so subsequent fetches respect the remote’s own auto‑tag rules.

**Desired Behaviour**
- On clone with default options, **no** `remote.<name>.tagopt` entry is written to the config.
- The initial clone must still download tags, matching the auto behaviour. This should be accomplished by adding a temporary fetch refspec `refs/tags/*:refs/tags/*` when the remote’s tag‑download policy is `AUTO`, without persisting that refspec.
- If the caller explicitly sets `remote_autotag` to `GIT_REMOTE_DOWNLOAD_TAGS_ALL`, the clone must write `remote.<name>.tagopt = --tags` and the remote’s `download_tags` becomes `ALL`.
- If the caller sets `remote_autotag` to `GIT_REMOTE_DOWNLOAD_TAGS_NONE`, the clone must write `remote.<name>.tagopt = --no-tags` and no extra tag refspec is added (no tags fetched).
- The default `remote_autotag` option when none is supplied should be `GIT_REMOTE_DOWNLOAD_TAGS_AUTO`.

**Implementation constraints**
- The change must only affect new clones; existing behaviour for saves, loads, and user‑specified options should remain unchanged.
- The temporary refspec for tags must be added after the origin remote is created, but only when the effective `download_tags` is `AUTO`. It must be added before connecting/starting the download, and must not be saved to disk.
- The existing fetch/download/update‑tips path should not be altered beyond inserting the temporary refspec.
- The `git_remote` object’s `download_tags` field must reflect the effective policy: `AUTO` when no option is passed, `ALL` when explicitly requested, etc.

**Acceptance criteria**
1. Default clone → `remote.origin.tagopt` absent from saved config; `download_tags` on the loaded remote is `AUTO`; tags are fetched.
2. Clone with `remote_autotag = ALL` → `remote.origin.tagopt = --tags` saved; `download_tags = ALL`.
3. Clone with `remote_autotag = NONE` → `remote.origin.tagopt = --no-tags` saved; `download_tags = NONE`; no extra tag refspec is added and no tags are fetched.

Do not write tagopt configuration option on clone by default


**Title:** Reflog entry messages containing newline characters are rejected rather than sanitized

**Problem Description**
When a caller provides a reflog entry message that includes one or more newline characters, `git_reflog_append()` immediately fails with an `GIT_ERROR_INVALID` error. Upstream Git does not reject such messages; it replaces newlines with spaces (for example, `git stash push -m "foo\nbar"` creates a reflog entry whose message is `"foo bar"`). This discrepancy causes unnecessary failures for programs that pass through user-supplied text or that match upstream behavior.

Additionally, the filesystem refdb backend’s serialization path (`serialize_reflog_entry`) currently does not check for newline characters at all. If a message containing newlines manages to reach that code, it will write the reflog file with multiple lines for a single entry, violating the reflog format and rendering the log unreadable.

**Expected Behavior**
- Any newline character (`'\n'`) in a reflog entry message should be replaced with a space character (`' '`). After replacement, any trailing whitespace should be removed so that the stored message does not end with a stray space.
- This sanitization must happen both when a new entry is appended via `git_reflog_append()` and when the filesystem backend writes a serialized reflog entry to disk. The sanitized message should remain a single line in the on-disk reflog file.
- Replacing newlines with spaces should be applied to all newline characters, including consecutive ones; for instance, `"foo\n\nbar\n"` would become `"foo  bar"` after replacement and trimming of the trailing space.

**Steps to Reproduce**
1. Obtain a `git_reflog` for a reference.
2. Call `git_reflog_append()` with a message string that contains a newline (e.g., `"line1\nline2"`).
3. Observe that the call fails with an error about invalid message.
4. Alternatively, attempt to write a reflog entry directly via the filesystem backend with such a message; the resulting reflog file will contain multiple lines for the entry and may be rejected by subsequent reads.

**Affected Versions**
All current versions of libgit2 that impose the newline restriction in `git_reflog_append()` and that lack sanitization in the filesystem serialization.

**Impact**
- Library users cannot match upstream Git behavior for stashes and other reflog operations when messages contain newlines.
- Programmatic consumers receive hard errors instead of a best-effort mapping to a valid single-line message.
- If a message with newlines is ever serialized without sanitization, the reflog file becomes malformed and can break reflog reading tools, including libgit2 itself.

**Suggested Resolution**
Replace every `'\n'` in the message with `' '` before storing it, then trim any trailing whitespace, in both `git_reflog_append()` and the filesystem backend’s `serialize_reflog_entry` function. Do not collapse multiple spaces into one; just perform the direct character replacement and trailing trim.

reflog: allow adding entries with newlines in their message


Fix saving of config files that contain multiline values (values with backslash-continued lines). When any change is written to such a config, the multiline value is lost or truncated instead of being preserved. The parser’s per-variable callback must report an accurate total line length covering all continuation lines, otherwise the write path cannot regenerate the original multiline content. Ensure that summing line lengths for continuation lines does not overflow.

Multiline config values not preserved on saving


**Title:** Allow proxy configuration when connecting with a detached remote

**Background:**
`git_remote_create_detached` creates a remote handle that is not associated with any repository. When such a remote is later used to connect (e.g., for credentials or ad-hoc network operations), the internal HTTP proxy resolution (`git_remote__http_proxy`) requires a non‑`NULL` repository pointer and skips proxy lookup entirely, effectively ignoring any global proxy settings the user may have set.

**Desired behavior:**
When a caller requests the HTTP proxy for a detached remote, the library should fall back to the user’s default (global/xdg/system) Git configuration to resolve proxy rules, instead of simply returning no proxy. Environment‑based proxy detection (like `HTTP_PROXY`) already works without a repository and must continue to work; the fix only concerns the config‑file‑based lookup.

**Constraints and details:**
- If `remote->repo` is `NULL`, the proxy lookup code should obtain a read‑only default config (typically the user’s global config) and use it to evaluate the standard `http.proxy`, `http.<url>.proxy`, and `remote.<name>.proxy` settings.
- The obtained config handle must be properly freed after the lookup.
- The existing `GIT_ASSERT_ARG(remote->repo)` check in the proxy path must be relaxed to allow a `NULL` repository.
- No new public API is required; the change is purely internal to the proxy resolution logic.
- The behavior for remotes that *do* have a repository must remain unchanged.

Allow proxy options when connecting with a detached remote.


When HEAD is orphaned (points to a nonexistent ref), `git_submodule_lookup` (and `git_submodule_foreach`) immediately fail with an error code, even though the repository contains a valid `.gitmodules` file and the submodules exist on disk and/or in the index.  The same failure occurs when the index is empty or missing.

**Expected behavior:**
- With an orphaned HEAD, lookup and iteration should still discover submodules that are defined in `.gitmodules`.  The only information that should be missing is the HEAD tree’s submodule pointer (`head_oid`); the submodule should otherwise be fully populated from the config, index, and workdir data.
- With an empty or missing index, submodules should still be found from `.gitmodules` and the workdir, merely lacking index‑derived OIDs and status.

In neither case should lookup itself fail — the absence of HEAD or index data is temporary state, not a fatal error.

**Observed behavior:**
- `git_submodule_lookup(name)` returns an error (often the error from the failed `git_repository_head_tree` call).
- `git_submodule_foreach` similarly aborts, never invoking the callback for submodules that *are* known.

**Impact:**
Any tool that inspects or manages submodules is broken on repositories that:
- are freshly created with `git checkout --orphan`,
- are in the middle of an initial commit,
- or have had their index removed/cleared.

Work with submodules (status, init, update, etc.) becomes impossible until a commit or a valid index is restored.

Test submodules with empty index or orphaned head


The `git_diff_format_email` and `git_diff_commit_as_email` functions place the caller-supplied summary into the Subject header of the generated patch email.
Currently, if the summary contains newline characters (`\r` or `\n`), the subject becomes multi-line, which violates the email format (RFC 2822) and can cause downstream tools to misinterpret the message.

**Expected behavior**

- The Subject line must always be a single line.
- When the `opts->summary` string contains a newline, only the text before the first newline (CR or LF) shall be used as the subject.
- If the summary is `NULL`, empty, or starts with a newline (so the sanitized subject would be empty), the function must return an error indicating that the summary is empty, instead of generating an email with a missing subject.
- The same sanitization must apply to all public APIs that format a patch email using the summary from `git_diff_format_email_options` (e.g., `git_diff_commit_as_email`).

This change ensures that the generated email is always well-formed, regardless of the input summary, without requiring callers to pre-sanitize the string.

Sanitize git_diff_format_email_options' summary parameter


When creating a new repository, automatically detect whether the underlying filesystem is case‑insensitive and set the `core.ignorecase` configuration variable accordingly. When re‑initializing an existing repository, the existing `core.ignorecase` setting must be preserved exactly as-is – no new detection or overwrite must occur.

Acceptance criteria:
- **New repository, case‑insensitive filesystem**: the generated repository configuration must contain `core.ignorecase = true`.
- **New repository, case‑sensitive filesystem**: the generated repository configuration must not set `core.ignorecase` (the key is absent or effectively false).
- **Re‑initialization (regardless of filesystem)**: the `core.ignorecase` value must remain unchanged from its prior state, even if it was manually changed or is missing.
- **Runtime detection**: the case‑insensitivity check must be done dynamically at repository‑creation time (e.g., by probing for a known, mixed‑case file relative to the git directory) rather than hard‑coded at compile time, so that environments with variable filesystem behaviour (per‑mount options, network drives, etc.) are correctly handled.
- **Scope**: the detection is performed only during the initial creation of a repository. Re‑initialization must skip the detection entirely and never rewrite `core.ignorecase`.

repository: make git_repository_init() value the core.ignorecase config entry


**Title:** `pack` backend ignores `multi-pack-index`, forces eager pack-file opening and slow object lookups

**Description:**
When a repository contains many pack files (for example after aggressive repacking or in large object databases), libgit2’s `pack` ODB backend performs every object existence check and read by scanning every individual `.idx` file. During this process the `.pack` files themselves are opened eagerly to build window caches, even when only a existence check is needed. This causes significant performance degradation and excessive file descriptor pressure compared to canonical Git, which uses the `multi-pack-index` file to locate objects across multiple packs without opening them.

In repositories where a `multi-pack-index` file exists under `objects/pack/`, libgit2 should:

- Detect and load the MIDX file on refresh, and keep it up‑to‑date when the file changes on disk (e.g. by validating its trailing checksum against a stat check).
- Give the MIDX priority when searching for an object (by full SHA1 or prefix). Only fall back to individually loaded pack indexes for packs that are **not** covered by the MIDX.
- Defer opening the actual `.pack` file until an object’s data must be read (i.e., lazy open). The MIDX provides the pack index and offset, making it possible to avoid opening packs entirely for existence checks or when the object turns out to be elsewhere.
- Handle prefix lookups correctly: an object that matches in both the MIDX and a non‑MIDX pack should still return `GIT_EAMBIGUOUS` as before; the MIDX must not silently shadow a valid match outside its packs.
- Gracefully degrade if the MIDX file is missing, corrupted, or references pack files that cannot be opened/verified. In those cases the backend should behave exactly as it does today (no MIDX), and a subsequent refresh should re‑attempt loading the MIDX.
- Maintain the separation of MIDX‑indexed packs from the “unindexed” pack list so that the same pack is never searched twice (once via MIDX and again via a duplicate in the individual pack list).

**Observed behavior:**
- Object lookups (e.g. `git_odb_read`, `git_odb_exists`) always open all `.pack` files and scan all `.idx` files, even when a `multi-pack-index` exists.
- File descriptor usage spikes because packs are opened even for simple existence checks.
- Performance is significantly worse than `git` in repos with many pack files.

**Expected behavior:**
- In repos with a valid `multi-pack-index`, object lookups should be fast and avoid opening unnecessary `.pack` files.
- The backend should automatically use the MIDX after a `refresh`, and fall back cleanly if the MIDX is unusable.

**Impact:**
- All callers using the default pack backend on repositories with multiple pack files suffer from poor performance and high resource usage. This is especially painful for applications that need to check many objects (e.g., status, diff, partial clones, push/fetch negotiation).

midx: Support multi-pack-index files in odb_pack.c


The `create_literal_schema` helper does not flatten nested `Literal` arguments when generating OpenAPI schemas. As a result, composite types such as `Literal[Literal["a", "b"], Literal[1]]` or type aliases that resolve to `Literal` (e.g., `Literal[ValueType, ConstType]` where both are themselves `Literal` aliases) are not expanded. The generated schema should treat these as if all leaf values were specified in a flat `Literal` — producing a single `enum` (or `const` if only one leaf remains) that contains every leaf literal value.

Update the literal schema generation so that it recursively extracts all leaf values from a `Literal` annotation before determining the schema representation.

Handle nested Literal annotations for OpenApi schema generation (#1829)


During route registration, paths containing malformed path parameters (e.g. `/{param}`, `/{param:foo}`, `/{:int}`, `/{param:}`) are accepted without error. The application starts successfully but later, when a matching request arrives, a cryptic runtime error occurs (e.g. KeyError in the parameter converter map, or empty parameter name). This makes debugging difficult and delays the failure.

**Expected behavior:**
A route registration using a path with an invalid parameter specification should immediately raise an `ImproperlyConfiguredException` with a clear message explaining the problem, so the developer can fix the path at startup. Specifically:

- A parameter must have the format `{name:type}` (colon required).
- The name part must be non‑empty.
- The type part must be one of the allowed types: `str`, `int`, `float`, `uuid`.

Any deviation from these rules should be rejected during registration.

**Impact:**
Currently, invalid path parameters slip through, causing confusing errors only when the route is actually hit, sometimes in production. This wastes development time and can hide bugs. Early validation would improve developer experience and safety.

Improve path param validation during registration


**Title:** Move `AllowedHosts` and `CompressionMiddleware` to handler-level middleware stack

**Background**
Currently `AllowedHostsMiddleware` and `CompressionMiddleware` are applied globally in `Starlite._create_asgi_handler`, meaning they intercept every incoming request (static files, OpenAPI endpoints, etc.) before the router even resolves a handler. This broad scope can cause unexpected side-effects with non-route-serving endpoints and makes it harder to reason about middleware ordering.

**Desired behavior**
Apply `CompressionMiddleware` and `AllowedHostsMiddleware` only to routes that are backed by registered route handlers (HTTP/WebSocket). They should be injected into the per‑route middleware stack created by `build_route_middleware_stack`, directly after any `CSRFMiddleware` and before any user‑defined route‑level middleware.

**Details**

- The application‑level configs (`compression_config`, `allowed_hosts`) remain attributes of the `Starlite` instance – only the wiring changes.
- In `Starlite._create_asgi_handler`, the wrapping with `CompressionMiddleware` and `AllowedHostsMiddleware` should be removed. The global `CORSMiddleware` stays untouched.
- Inside `build_route_middleware_stack` (`starlite/asgi/routing_trie/mapping.py`), after the optional `CSRFMiddleware` block, add:
  - `CompressionMiddleware` when `app.compression_config` is truthy.
  - `AllowedHostsMiddleware` when `app.allowed_hosts` is truthy.
- The resulting middleware order per route becomes:
  `ExceptionHandler` → (`CSRF`?) → (`Compression`?) → (`AllowedHosts`?) → user middleware → handler.
- No changes to public API or configuration schemas. Existing tests for allowed hosts and compression should continue to pass once updated to inspect the handler‑stack rather than the global `asgi_handler`.

**Acceptance Criteria**

- A request to a handler‑less endpoint (e.g. the OpenAPI schema, static files) does *not* pass through `AllowedHostsMiddleware` or `CompressionMiddleware`.
- A request to a regular route still obeys the allowed‑hosts rules and applies compression as before.
- The middleware is injected only when the corresponding config object is present on the `Starlite` application instance.

Move AllowedHosts and CompressionMiddleware application to handler level (#804)


**Title:** OpenAPI schema incorrectly drops HTTP methods when multiple route handlers share the same path

**Description**
When multiple route handlers are registered for the same URL path (e.g., a `GET` handler and a `POST` handler both bound to `"/"`), the generated OpenAPI schema for that path item only includes the operation for the last-registered handler. All other HTTP methods are missing from the path item, even though all handlers are valid and reachable at runtime.

**Steps to reproduce**
1. Create two handler functions, one for `GET` and one for `POST`, both targeting the same path `"/"`.
2. Pass both handlers to a `Litestar` application.
3. Retrieve the OpenAPI schema (e.g., via `OpenAPIPlugin.provide_openapi()` or by serving `/schema`).
4. Inspect the `/` path item.

**Expected behavior**
The generated path item for `"/"` contains an operation object for every HTTP method that has a registered handler (e.g., both `.get` and `.post` are populated).

**Actual behavior**
Only the last-registered handler’s HTTP method appears; all others are missing.

**Context**
This appears to be caused by how `OpenAPIPlugin` collects and merges route information during schema construction. The plugin maintains an internal collection of routes, but when multiple `HTTPRoute` objects share the same path, only one can contribute to the final path item under the current logic, discarding the other methods.

**Acceptance criteria**
- The OpenAPI path item for any route correctly aggregates all HTTP methods across all handlers registered for that route’s path.
- No changes to handler registration or route layering; the fix should be internal to the OpenAPI schema generation.
- The behavior is verified for the common pattern where a single `Litestar` route is created with multiple `route_handler_map` entries (e.g., from different handler functions on the same path).

fix(openapi): correctly process routes with multiple route handlers (#2864)


**Title:** OpenAPI schema omits `minItems`/`maxItems` for msgspec collection fields constrained via `Meta`

**Background**
When a msgspec `Struct` field uses `Annotated[list[T], msgspec.Meta(min_length=N)]` (or any collection type: `set`, `frozenset`, variable-length `tuple`), runtime validation correctly rejects arrays that violate the length constraints, and `msgspec.json.schema()` correctly emits `minItems`/`maxItems`.  However Litestar’s own OpenAPI schema generation ignores these constraints for collection types.

**Desired behavior**
`minItems` and `maxItems` must appear in the generated OpenAPI schema for msgspec struct fields whose `Meta` specifies `min_length` and/or `max_length` on a supported collection type.

**Affected collection types**
- `list[T]`
- `set[T]`
- `frozenset[T]`
- variable-length `tuple` (i.e., `msgspec.inspect.VarTupleType`)

**Where the mapping should happen**
The function `kwarg_definition_from_field` in `litestar/plugins/core/_msgspec.py` already inspects `msgspec.inspect.Field` type objects and translates `max_length`/`min_length` into `ParameterKwarg` kwargs for string/bytes types.  For collection types (`ListType`, `SetType`, `FrozenSetType`, `VarTupleType`), the same `min_length`/`max_length` attributes exist but are currently dropped.  They should be mapped to `ParameterKwarg`’s `min_items` and `max_items` kwargs, which the schema engine already maps to `minItems`/`maxItems` (see `KWARG_DEFINITION_ATTRIBUTE_TO_OPENAPI_PROPERTY_MAP`).

**Constraints**
- Only affect the collection types that carry `min_length`/`max_length` (i.e., skip mapping when they’re `0`/`None` unless explicitly set, but follow the existing pattern—just set the kwarg directly).
- No change to non-`Struct` field definitions or other libraries (Pydantic, attrs).
- Must remain compatible with the existing translation chain: `msgspec.inspect.Field` → `ParameterKwarg` → OpenAPI `Schema` properties.

fix: OpenAPI schema missing minItems/maxItems for msgspec Meta on collections (#4605)


When a route handler accepts a parameter annotated with a subscribed generic (e.g., `Foo[str]`) and provides a default value of that type, validation crashes with an internal server error. The root cause is that `isinstance(value, Foo[str])` raises a `TypeError` because `Foo[str]` is a parameterized generic, not a plain type.

Ensure that both the signature model deserializer (`_deserializer`) and the default JSON/msgpack deserializer gracefully handle subscribed generics. When the `isinstance` check raises `TypeError`, extract the unparameterized origin class via the existing `get_origin_or_inner_type` utility and retry with that origin. If the value is an instance of the origin, accept it without further deserialization; otherwise, re-raise the original error. This prevents the 500 error and allows routes with such parameters to succeed.

fix(validation): fix handling validation of subscribed generics (#3519)


**Problem**
Running `litestar --app my_app:app --help` omits commands registered by CLI plugins (implementing `CLIPluginProtocol`). The same happens with `--app-dir`. Plugin commands appear only when executing a concrete command (not `--help`), because app loading is skipped when the help flag triggers.

**Expected behavior**
When `--help` is combined with an explicit `--app` (or `--app-dir`) that points to a valid Litestar application, the help output must include all commands added by plugins via `on_cli_init`. This should work for the top-level group and any subcommand group.

**Acceptance criteria**
1. `litestar --app <path> --help` shows plugin-provided commands with their descriptions.
2. Using `--app-dir <directory>` alongside `--help` works identically.
3. If the application cannot be loaded (e.g., invalid path, no app found), `--help` still displays built-in commands without crashing.
4. The application is loaded at most once per invocation; performance and correctness of non-help commands remain unchanged.
5. The fix does not alter the behavior of commands invoked without `--help`.

fix(cli): pre parse launch arguments when cli command executed (#4161)


**Describe the bug**
When a handler parameter has a default value but does not use `Annotated` to attach metadata, the generated OpenAPI schema for that parameter omits the `default` key. In contrast, if the same default is provided via `Annotated` (e.g., `Annotated[int, Parameter(default=10)]`), the default appears correctly in the schema.

**To Reproduce**
```python
from litestar import Litestar, get

@get("/")
async def get_foo(foo_id: int = 10) -> None: ...

app = Litestar([get_foo])
print(app.openapi_schema.paths["/"].get.parameters[0].default)
# Expected: 10
# Actual: None
```

**Expected behavior**
The OpenAPI schema should include the parameter's default value (e.g., `"default": 10`) even when no `KwargDefinition` (i.e., no `Annotated` metadata) is present. The default value is already available on the generated `FieldDefinition` and should be used as a fallback.

**Impact**
- API consumers / client generators rely on default values from the OpenAPI specification to auto-fill optional parameters. Missing defaults lead to incorrect client code or unnecessary manual overrides.
- The inconsistency between explicit `Parameter(default=...)` and plain Python defaults violates the principle of least surprise: if I write `foo_id: int = 10`, I expect that default to be reflected in the schema.

**Additional context**
Currently, the schema creation logic only propagates defaults when they are set on a `KwargDefinition` (such as `Parameter`, `Body`, etc.). When no `KwargDefinition` exists – because no extra metadata was provided via `Annotated` – the default from the `FieldDefinition` is ignored. A fix should ensure that whenever a `FieldDefinition` has a non-`Empty` default and the generated schema does not already have a default set, the field's default is used as the schema's default.

fix(openapi): set default on schema from `FieldDefinition` (#3280)


When configuring `JWTCookieAuth` or `OAuth2PasswordBearerAuth` with a `domain` value (e.g., `.example.com`), the login response cookie does not include that domain. This prevents the authentication cookie from being shared across subdomains, even though the `domain` attribute is explicitly set on the auth backend configuration.

Additionally, the cookie’s expiration is currently expressed as an absolute `expires` value computed as a Unix timestamp, which may be misinterpreted or lead to clock-sensitivity issues. The cookie should instead convey its lifetime using the `max-age` directive, specifying the token’s duration in seconds.

**Acceptance criteria**

- When a non‑`None` `domain` is set on the auth config instance (either `JWTCookieAuth` or `OAuth2PasswordBearerAuth`), the `Set-Cookie` header returned by the login endpoint must include the `Domain` attribute with that exact value.
- When `domain` is `None` (the default), no `Domain` attribute is present in the `Set-Cookie` header (unchanged behaviour).
- The cookie set by the login endpoint uses the `max-age` directive, with a value equal to the token expiration duration in seconds (i.e. `(token_expiration or default_token_expiration).total_seconds()`), instead of an absolute `expires` timestamp.
- The change applies to both `JWTCookieAuth.login` and `OAuth2PasswordBearerAuth.login` responses.
- All other cookie attributes (`Path`, `HttpOnly`, `Secure`, `SameSite`) remain as configured and are unaffected by this fix.

fix: JWT Auth correctly handles `domain` configuration value. (#1223)


We need to include `Access-Control-Allow-Headers` and `Access-Control-Allow-Methods` headers in all responses from the `CORSMiddleware` (not just preflight responses), when the origin is allowed and the headers aren’t already present from the preflight handling.

## What needs to change

In `CORSMiddleware.send_wrapper`, after the existing logic that sets `Access-Control-Allow-Origin` and `Access-Control-Expose-Headers`, also set `Access-Control-Allow-Headers` and `Access-Control-Allow-Methods` from the `CORSConfig.allow_headers` and `CORSConfig.allow_methods` lists, respectively.

### Behaviour to implement

- For every **non‑preflight** HTTP response where the request `Origin` is present and the origin is allowed:
  - If `config.allow_headers` is non‑empty, add `Access-Control-Allow-Headers` with the sorted, comma‑separated, deduplicated list of headers.
  - If `config.allow_methods` is non‑empty, add `Access-Control-Allow-Methods` with the sorted, comma‑separated, deduplicated list of methods.
- These headers must **not** be added when the `Origin` is absent or the origin is not allowed (existing behaviour for other CORS headers, no change here).
- The preflight path (`scope.method == "OPTIONS"`) already sets these headers via `config.preflight_headers`. Therefore the `send_wrapper` must **not overwrite** them when they are already present in the outgoing header dictionary. Only add them if the key is missing.

### Motivation / why this matters

Currently only preflight responses include `Access-Control-Allow-Headers` and `Access-Control-Allow-Methods`. Many CORS flows and browser implementations expect these headers to be present in the actual response (especially when credentials are involved or when preflight caching isn’t used). Without them the configured allowed headers/methods are invisible to the client after the preflight, potentially causing failures.

### References

- `CORSConfig.allow_headers` (default `["*"]`)
- `CORSConfig.allow_methods` (default `["*"]`)
- `CORSConfig.preflight_headers` already includes the correct values.
- The `CORSMiddleware.send_wrapper` currently only sets `Access-Control-Allow-Origin`, `Access-Control-Allow-Credentials`, `Access-Control-Expose-Headers`, and `Vary`. It never adds the allow‑headers / allow‑methods.

### Test expectations (not part of the request, just for context)

Update `test_cors_simple_response` to also provision `allow_headers` and `allow_methods` in `CORSConfig` and assert:

- The headers are present when the origin is allowed (and absent when it is not).
- The values are sorted and deduplicated.
- The wildcard `"*"` is correctly passed through.

---

Please implement the missing header injection in `CORSMiddleware.send_wrapper`.

fix: missing cors headers in response (#3179)


Refactor `DTOInterface` so that DTO instances are bound to the connection lifespan, not to individual messages. The DTO should be instantiated once per connection and reused for all parsing and serialization on that connection.

- Remove classmethod constructors (`from_bytes`, `from_data`) and replace with:
  - `__init__` that receives the connection.
  - Instance method `bytes_to_data_type(raw: bytes) -> Any` for inbound parsing.
  - Instance method `data_to_encodable_type(data: Any) -> bytes | LitestarEncodableType` for outbound serialization.
- Remove the `to_data_type` and `to_encodable_type` instance methods that operated on pre‑constructed DTO state.
- Remove `DTOInterface` from the global type encoders in `litestar.serialization`; DTOs should return bytes or an encodable type directly, not rely on a special encoder.
- Update the HTTP data handler to create the DTO with `return_dto(connection)` and call its `data_to_encodable_type`.
- Update the DTO kwarg extractor to create the DTO with `dto_type(connection)` and call its `bytes_to_data_type`.
- Ensure backwards compatibility for any existing websocket handler that relies on a connection‑bound DTO for repeated parse/serialize over the connection’s lifetime.

Refactor `DTOInterface` - a better pattern for websockets. (#1539)


**Fix cookie caching causing stale Set-Cookie headers and stabilize flaky CORS test**

`_normalize_cookies` in `starlite/handlers/http.py` is decorated with `lru_cache`, which reuses previously computed cookie lists across requests. This causes per-request dynamic values (e.g., cookies set with a newly generated UUID) to be replaced by stale values from the cache. The cache must be removed so that cookie normalization always runs fresh for each request, preserving the intended dynamic behaviour.

Also, the hypothesis-based CORS middleware test (`test_cors_simple_response`) occasionally hits a deadline problem. Add `@settings(deadline=None)` to eliminate the deadline check and make the test stable.


The `create_engine` method in `GenericSQLAlchemyConfig` attempts to handle dialects that do not support JSON type by removing `json_serializer` and `json_deserializer` and retrying. Currently it catches `ValueError` for the first call, but the actual exception raised by SQLAlchemy when a dialect rejects these JSON parameters is `TypeError`. Change the except clause from `ValueError` to `TypeError` so that the fallback path is triggered.

fix: handle dialects that don't support json (#2139)


**Title:** DTOFactory mishandles pydantic fields defined with `...` (required) as default

**Background**
When constructing DTOs, `DTOFactory.__call__` inspects the source model’s fields to build `field_definitions`. It currently checks whether a field’s `field_info.default` is `pydantic.fields.Undefined` to decide if the field has a concrete default value.

In pydantic, a field may also use `...` (Ellipsis) to declare that the field is required and has no default – this is the standard way to mark a field as mandatory and reject `None` (i.e. `allow_none=False`). The current logic treats `...` as a real default value, causing the generated DTO to incorrectly assign that default and consequently lose the `allow_none=False` constraint.

**Problem**
- Source models with required fields (e.g., `password: str = ...` or `create_model("Example", password=(str, ...))`) result in a DTO where the required field is no longer required.
- The generated DTO field allows `None`, breaking validation expectations.

**Expected Behavior**
The DTO factory should recognise both `pydantic.fields.Undefined` and `Ellipsis` as sentinels meaning “no default”, so that:
- The field is NOT placed into `field_definitions` with a concrete default.
- The generated DTO field retains the original `allow_none=False` setting.
- Required fields remain required in the generated DTO model.

**Ask**
Update the default-detection condition inside `DTOFactory.__call__` (the branch where `field_definitions` are populated) to treat `...` identically to `Undefined`, so that a required field from the source stays required in the generated DTO.

Treats `...` equivalent to `PydanticUndefined` in context of detecting presence of a specified default.


The `SQLAlchemyPlugin`’s `to_pydantic_model_class` should produce a set of Pydantic models that faithfully mirror the relationships declared on the SQLAlchemy ORM models, including both bidirectional and self-referential relationships. Currently the plugin leaves some relationship fields typed as `Any` or fails to correctly link the generated Pydantic types across related entities, forcing external callers to manually patch forward references or regenerate models.

**Acceptance criteria**

1. **Bidirectional relationships are correctly typed on both sides.**
   Given a parent `User` with a one‑to‑many `pets` relationship that has `back_populates="owner"`, and a child `Pet` with an `owner` relationship pointing back to `User`:
   - The generated Pydantic model for `User` must have a `pets` field typed as `List[Pet]` (where `Pet` is the generated Pydantic model).
   - The generated Pydantic model for `Pet` must have an `owner` field typed as `User` (the same Pydantic model class as the parent).

2. **Self‑referential relationships resolve to the same model.**
   If `User` has a many‑to‑many `friends` relationship that references `User` itself, the `friends` field of the generated Pydantic `User` model must be typed as `List[User]` (a list of the model that contains the field).

3. **No duplication of Pydantic models for the same SQLAlchemy entity.**
   Each SQLAlchemy declarative entity maps to exactly one Pydantic class; subsequent references within the same conversion invocation must reuse the same class object.

4. **All forward references are resolved inside the conversion.**
   After `to_pydantic_model_class` returns, the resulting Pydantic model must have its forward references fully resolved, so that `model.__fields__["some_relation"].type_` is a concrete Pydantic class (not a `ForwardRef` or `Any`). Callers should not be required to call `model.update_forward_refs()` explicitly.

5. **The conversion must not enter infinite recursion when encountering cycles** (e.g., `User.pets` → `Pet.owner` → `User`).


We need OpenAPI schema generation for Pydantic V1 models (both plain `BaseModel` and `GenericModel` subclasses) to tolerate private attributes that carry unresolvable type annotations. This is required because Pydantic V1 models may define private attributes under the `underscore_fields_are_private` Config setting, and those attributes sometimes use placeholder or unknown type names. The schema generator should never attempt to resolve those annotations; private attributes should be excluded from the schema without causing a `NameError` or any other exception.

**Acceptance criteria:**

1. **Public-only schema content:** When a Pydantic V1 model with `Config.underscore_fields_are_private = True` is processed by the `PydanticSchemaPlugin`, the resulting OpenAPI schema must not contain any private fields. Private fields are those whose names start with `_` and are marked as private by Pydantic’s configuration.

2. **Tolerance of invalid private annotations:** If a private field’s type annotation references a name that is not defined (e.g., `_foo: "bar"` where `bar` is not a valid type), the schema generation must succeed. No `NameError` or other exception should be raised.

3. **Generic model support:** The same guarantees must hold for Pydantic V1 `GenericModel` subclasses. Private fields on a concrete generic model (e.g., `class Model(GenericModel, Generic[T]): ... _field: str = PrivateAttr()`) must be silently excluded without attempting to resolve their annotations.

4. **No regression for valid public annotations:** Normal public fields with correctly defined types must continue to appear in the schema, and all existing generic resolution behavior must remain unchanged.

5. **V2 models unaffected:** Pydantic V2 models (including generic ones) already handle private field exclusion correctly; this request is explicitly scoped to the V1 pathway in `pydantic_get_type_hints_with_generics_resolved` within `litestar.contrib.pydantic.utils`.

fix(pydantic): Pydantic V1 schema generation for PrivateAttr in GenericModel (#3161)


Make `SignatureModel.parse_values_from_connection_kwargs` accept a single `kwargs: dict[str, Any]` argument instead of spreading parameters as `**kwargs`. This prevents collisions with the method's own formal parameters (`connection`, `deserializer`, `kwargs`, etc.) when a route handler or dependency declares a parameter with one of those names. All call sites (HTTPRoute, WebSocketRoute, dependency resolution) must pass the parsed parameters as a dictionary.

fix(signature): Fix #3593: Ensure signature model internal function signatures don't clash with model signature (#3605)


We noticed that `DTOConfig.rename_fields` with dot‑separated nested paths (e.g. `"bar.id": "bar_id"`) is silently ignored – only top‑level renames take effect. When a DTO is built for a dataclass that contains another dataclass, or a list/dict of such inner types, the configured nested rename is not applied during serialization (output) nor deserialization (input).

**Expected behaviour**

- Defining `DTOConfig(rename_fields={"parent.nested_field": "new_name"})` (or `{"parent.0.inner": "outer_inner"}` for collections of nested types) must cause the DTO to accept and produce data using the renamed key for those nested members.
- The renaming must correctly apply to both top‑level fields and fields inside nested models, including those inside `List`, `Dict`, `Tuple`, and `Union` types.
- The feature must play nicely with the existing `rename_strategy` (e.g. camelCase) and with partial updates (`partial=True`).

**Scope / constraints**

- Only the DTO backends (`DTOBackend` and whatever replaces it) need to be fixed – the public API and configuration options (`DTOConfig.rename_fields`, `DTOConfig.rename_strategy`) stay the same.
- The fix must not break any existing top‑level renaming, and must correctly recurse through composite types (collections, mappings, tuples, unions) so that every nested field definition receives the appropriate subset of `rename_fields` (filtered by the current nesting prefix).

**Acceptance criteria**

1. Given a model like `Foo` with a nested `Bar` field, and `DTOConfig(rename_fields={"id": "foo_id", "bar.id": "bar_id"})`, both `foo_id` and `bar_id` appear as keys in the serialized output, and the DTO successfully deserialises input that uses those keys.
2. The same holds when the nested field is inside a collection, e.g. a `List[Bar]` with `"bars.0.id": "bars_id"`.
3. Nested renaming works for `Dict` keys/values and `Tuple` elements as well (e.g. `"mapping_key"` or `"0.field"`).
4. The existing top‑level rename tests continue to pass unchanged.
5. The fix does not alter the runtime behaviour when no nested renames are configured.

fix(dto): nested field renaming (#2721) (#2764)


The logging middleware configured with structlog raises an unhandled exception and returns HTTP 500 when the request body is malformed (e.g., invalid JSON).  The middleware should gracefully handle body parsing failures so that the application can still return the appropriate 4xx response and the request log is populated with the raw body bytes instead of crashing.

fix(3063): Logging middleware with structlog causes application to return HTTP 500 when request body is malformed (#3109)


**Title:** ValidationException detail exposes full connection URL (including scheme and host) instead of only the path

**Description:**

When a request fails parameter validation, Litestar raises a `ValidationException` whose `detail` message can include the full client connection URL, e.g.:

```
Missing required query parameter 'param' for url http://internal-service.local/api/v1/resource?other=value
Validation failed for POST http://internal-service.local/api/v1/resource
```

Exposing the complete URL (scheme, host, port) in error responses leaks internal network details and hostnames that should not be visible to clients or intermediate proxies. Only the path (and query string) is needed to identify the failing request.

**Expected behavior:**

The error detail should contain only the relative part of the URL – the path and query string – without scheme or host. For example:

```
Missing required query parameter 'param' for path /api/v1/resource?other=value
Validation failed for POST /api/v1/resource
```

**Affected areas (observable):**

- `create_connection_value_extractor` (missing required parameter error)
- `SignatureModel._create_exception` (validation failure error detail)

**Acceptance criteria:**

- When a required path/query/header/cookie parameter is missing, the `detail` says `... for path <path?query>` instead of `... for url <full-url>`.
- When signature validation fails, `detail` says `Validation failed for <METHOD> <path?query>`, omitting the scheme and host.
- Query string is preserved (if present) and appears immediately after the path, i.e., `?key=value`.
- No other information (scheme, host, port) is included.

feat: only display path in ValidationExceptions (#3064)


**Feature request: Support multiple path values per route handler**

**Request**
Allow route handlers (e.g., `@get`, `@post`, WebSocket handlers, and controller route handlers) to accept a list of paths, so that a single handler definition can serve multiple URL endpoints.

**Acceptance criteria**

1. **Parameter type**
   The `path` argument of `BaseRouteHandler` (and its subclasses `HTTPRouteHandler` and `WebSocketRouteHandler`) accepts either:
   - A single `Optional[str]` (backward‑compatible behaviour), or
   - A `Optional[List[str]]` (new behaviour).

2. **Normalization**
   Every path in the list must be normalized using the existing `normalize_path` utility (trailing slashes, etc.), exactly as a single path is normalized today.

3. **Registration**
   When a route handler is registered with the `Router`, it must be added to the route map for **every** path in the list.
   - For HTTP handlers: each path maps to the handler’s `{method: handler}` entry.
   - For WebSocket handlers: each path maps directly to the handler.
   - For controller-scoped handlers: each handler path (from the list) is combined with the controller’s base path using `join_paths`, and each resulting combined path is registered.

4. **Observable behaviour**
   A request to any of the specified paths reaches the same handler function and must not cause routing errors or mis‑matches.

5. **No other changes**
   The existing single‑path behaviour and all other handler functionality (dependencies, guards, response handling, etc.) remain unchanged.

**Non‑obvious edge cases / safety constraints**
- If `None` is passed for `path`, the default `"/"` should be used for each element.
- The internal representation should store paths as a list internally (e.g., `paths: List[str]`) to avoid duplicate logic; direct external access to a `path` property may need adjusting to prevent breaking existing code.
- When a controller registers its handlers, the iteration must respect the handler’s multiple paths; the controller path is joined to each handler path individually.


**Title:** Improve error message when OpenAPI schema generation fails for generic handler parameter types

**Background**
When a route handler includes a parameter whose type annotation resolves to a user-defined generic class (e.g., `MyGeneric[int]`) and that parameter is *not* wrapped with `Dependency(...)`, the OpenAPI schema generation step inside `create_schema` fails with a cryptic `KeyError` while looking up `field.shape` in `PYDANTIC_FIELD_SHAPE_MAP`. This is misleading because the root cause is usually a missing dependency declaration, not an internal schema mapping bug.

**Desired Behavior**
Catch this specific failure and raise `ImproperlyConfiguredException` with a clear, actionable message. The exception should:

- Mention the parameter name and its resolved type (`field.outer_type_`).
- Explain that the type could not be mapped to an OpenAPI type.
- Suggest that if the parameter is meant to be injected via dependency injection, it should be annotated using `Dependency(...)`, e.g. `param: MyGeneric[int] = Dependency(...)`.

This makes the configuration error immediately understandable and avoids developers having to decode framework-internal mapping dicts.

**Implementation Notes**
- The mapping from `field.shape` to `OpenAPIType` is performed in the `create_schema` flow where complex types are handled.
- The check should happen after `field.sub_fields` is truthy but before the schema is built, mirroring the current logic that tries to lookup the shape in `PYDANTIC_FIELD_SHAPE_MAP`.
- Any `KeyError` from that lookup for an unexpected shape should be caught and re-raised as `ImproperlyConfiguredException` using the field’s name and outer type in the error detail.
- The change should not alter the behavior for types that are correctly mapped (e.g., `List`, `Dict`, `Set` etc.).

Improper config when user-defined default type provided as parameter (#136)


```
### Bug Report: OpenAPI schema incorrectly includes `content` for `ResponseSpec(None)` on no-body responses

**Problem**
When a route handler explicitly declares a response with `ResponseSpec(None)` (e.g., for status `204 No Content`), the generated OpenAPI specification includes a `content` key for that response, even though the response has no body. This violates the OpenAPI 3.x specification, which states that a `content` field MUST NOT be present for responses with no payload (like 204 or 304).

**Example**
```python
@delete(
    path="/resource",
    status_code=HTTP_204_NO_CONTENT,
    responses={204: ResponseSpec(None, description="Resource successfully deleted")},
)
def delete_resource() -> None:
    return None
```

Currently this produces an OpenAPI response object like:
```json
"204": {
  "description": "Resource successfully deleted",
  "content": {
    "application/json": {
      "schema": {"type": "null"}
    }
  }
}
```

The `content` block is both semantically wrong and non-conformant.

**Expected behavior**
For any `ResponseSpec` where the data container is `None` (i.e., no body), the resulting OpenAPI response object should omit the `content` key entirely. For the example above, the output should be:
```json
"204": {
  "description": "Resource successfully deleted"
}
```

**Impact**
- Produces invalid OpenAPI schemas that may be rejected by strict validators and client generators.
- Prevents correct documentation of no-content responses when a custom description, headers, or other metadata are required.
- Users are forced to either accept the incorrect schema or abandon `ResponseSpec` for these cases, losing declarative documentation.

**Additional context**
The `ResponseSpec` `data_container` parameter currently accepts `None` but the OpenAPI generation code unconditionally creates a schema from it, leading to the unwanted `content`. Making the generation logic aware that `None` signifies “no content” (and skipping `content` in that case) would fix the issue. This should apply only when the entire data container is `None`; explicit `NoneType` or `Optional[...]` with `None` allowed should still produce schemas when appropriate.
```

fix: Generate correct OpenAPI response for `ResponseSpec(None)` (#3069) (#3098)


The `litestar routes` CLI command currently builds its Rich tree inline within the handler, mixing route filtering and rendering concerns. Extract the tree-generation logic into a dedicated, private helper class so that the tree structure can be tested independently from the CLI runner, without changing the command’s output. The new class must accept the sorted, filtered route list and handle all three route types: HTTP routes (including sub-path handlers and sync/async/HTTP-method annotations), WebSocket routes (labeled *WS*), and ASGI routes (labeled *ASGI*). The command itself should only be responsible for sorting, optional schema‑route filtering, and exclusion, then delegate entirely to the tree builder.

refactor(CLI): `routes` command and associated tests (#3003)


I'm using `RedisStore` from Litestar to cache values, and I need to update an existing value while keeping its original TTL unchanged. The redis client supports the `KEEPTTL` option on `SET` for exactly this use case, but `RedisStore.set` doesn't expose it. The only way to set an expiration is via `expires_in`, which overwrites the current TTL.

Additionally, if we attempt to pass both a new `expires_in` and request `keepttl`, the semantics are contradictory and Redis will reject it (or behave unpredictably). We should prevent that configuration rather than letting a cryptic redis error bubble up.

Desired behavior:

- Add an optional `keep_ttl: bool` parameter to `RedisStore.set`, defaulting to `False`.
- When `keep_ttl=True`, the `set` call must preserve the key's existing TTL (using Redis `KEEPTTL`).
- When `keep_ttl=True` and `expires_in` is also passed, raise a `ValueError` immediately, because these options are mutually exclusive.
- The standard `expires_in`-only usage should remain unchanged when `keep_ttl=False`.

This would give callers a safe way to update a value without accidentally resetting its expiration, and prevent silent foot-guns when mixing the two settings.

fix(stores): validate mutually exclusive expires_in and keep_ttl in RedisStore.set (#4562)


The `Router`'s registration validation has several gaps that allow silent misconfiguration or produce incorrect error messages:

- **Self‑registration uses equality instead of identity**: `validate_registration_value` checks `value == self` to detect a router being registered on itself. This causes a false‑positive `ImproperlyConfiguredException` when another `Router` instance happens to have the same path and handler configuration but is not the same object. It should use `value is self`.
- **No validation for non‑handler types**: Passing a plain class that is not a `Controller`, `RouteHandler`, or `Router` is accepted without an immediate error, leading to obscure failures later in the request lifecycle.
- **No validation for already‑owned routers**: A `Router` instance that already has an `owner` (i.e., it has been registered in another router) can be passed again without raising an error, resulting in unpredictable routing behaviour.

Additionally, `Provide.__eq__` does not short‑circuit on identity, which can lead to unnecessary attribute comparisons and subtle correctness issues when comparing the same object.

**Expected behaviour**
- Registering a `Router` on itself (the exact same instance) must raise `ImproperlyConfiguredException` with a clear message.
- Registering a different `Router` instance that merely compares equal by its configuration must succeed.
- Passing a value that is not a `Controller`, `RouteHandler`, or `Router` must raise `ImproperlyConfiguredException` immediately.
- Registering a `Router` that already has an `owner` set must raise `ImproperlyConfiguredException` because a router can only be owned by one parent.
- `Provide.__eq__` should return `True` immediately when `other is self`, avoiding attribute access on a potentially unrelated object.

These validations should be enforced inside `Router.validate_registration_value` (or equivalent) so that configuration errors are caught eagerly during application setup, rather than at request time or through confusing error messages.


When defining dependencies at multiple levels (router, controller, handler) using `Provide`, the framework does not gather all dependencies from ownership layers before resolving them. A dependency that is needed by another dependency but not directly requested by the route handler’s own signature is never made available, causing the injection to fail.

**Observed behavior**
If a router declares `top_dependency`, a controller declares `mid_dependency` (which itself depends on `top_dependency`), and the handler only asks for `mid_dependency` (or a computed dependency that consumes both), `top_dependency` is not included in the resolved dependencies. The `resolve_dependencies` method in `BaseRouteHandler` only collects dependencies whose key appears in the handler’s signature model, ignoring lower-layer dependencies that are needed indirectly.

**Expected behavior**
All dependencies from every layer in the ownership chain (router → controller → handler) should be collected into the resolved providers dictionary. When `resolve_signature_kwargs` recursively resolves dependencies, it should be able to supply any required dependency from the full set, regardless of whether it appears in the handler’s own parameter list. This enables a dependency graph where, for example, a controller’s dependency can rely on a router-level dependency, and the handler simply receives the final result.

**Impact**
This blocks natural composition of dependencies across layers and forces workarounds (duplicating dependency declarations at every level or restructuring the dependency graph). It contradicts the stated recursive resolution behaviour.

**Suggested fix**
In `BaseRouteHandler.resolve_dependencies`, remove the guard that checks `if key in field_names` before adding a provider. The recursive resolution in `resolve_signature_kwargs` already handles walking the dependency tree, so all that is needed is to collect every dependency from all layers unconditionally, with the usual uniqueness validation.


We should let users configure the `oauth2RedirectUrl` that Swagger UI uses, because the default page-relative computation breaks when the API is served under a sub-path behind a reverse proxy. Currently, `SwaggerRenderPlugin` hard-codes the Swagger UI configuration without any way to override this URL, making OAuth2 flows unusable in such deployments.

**Acceptance criteria:**
- `SwaggerRenderPlugin` gains a new optional constructor parameter `oauth2_redirect_url` (type `str | None`, default `None`).
- When the parameter is provided, the rendered Swagger UI initialization must include `oauth2RedirectUrl: '<value>'` in the `SwaggerUIBundle` options.
- When the parameter is omitted or `None`, the rendered output must remain unchanged (no `oauth2RedirectUrl` key emitted), preserving Swagger UI’s default behavior of computing the URL from the current page location.
- The provided value should be emitted as an absolute URL string; no transformation or validation is required. Users are expected to supply the correct absolute URL, typically pointing to the `oauth2-redirect.html` file already served by the OpenAPI plugin under the schema path.

feat(openapi): add oauth2_redirect_url parameter to SwaggerRenderPlugin to allow API mounting under a sub-path (#4676)


The `state` reserved kwarg silently accepts invalid type annotations, leading to confusing runtime behavior instead of a clear startup error.

**Problem**
In Starlite, route handlers and controller methods can declare a parameter named `state` to receive the application’s `State` object. When this parameter is given an incompatible type annotation (e.g. `state: str` or `state: int`), the framework does **not** raise any error at application startup. The application initializes successfully, and the error only appears later—either when the endpoint is called (with a cryptic error or unexpected `None`) or when the parameter binding fails in opaque ways.

This violates the principle of failing fast and gives no guidance that the `state` kwarg must be typed as a subclass of `ImmutableState`.

**Expected behavior**
When a handler defines a parameter named `state`, the framework should validate that its type annotation is a subclass of `ImmutableState` (including `State` itself). If the annotation does **not** satisfy that requirement, an `ImproperlyConfiguredException` should be raised immediately during handler registration, with a clear message like:

```
The type annotation <invalid_type> is an invalid type for the 'state' reserved kwarg.
It must be typed to a subclass of `starlite.datastructures.ImmutableState` or `starlite.datastructures.State`.
```

This ensures the developer sees the problem before any request is served.

**Context**
- Reserved kwargs like `request` and `scope` already receive similar validation.
- The check should be enforced inside the signature parsing logic (e.g., `parse_fn_signature` in `starlite/signature/parsing.py`).
- The fix should handle direct annotations (e.g., `state: State`) and should not regress for correctly typed `state` parameters.

Check the type annotation of the `state` reserved kwarg. (#1264)


**Title:** DTO handlers reject `Sequence`, `Mapping`, and similar generic collection type annotations with a RuntimeError

**Description:**

When a route handler parameter is annotated with a generic collection type such as `Sequence[User]`, `Mapping[str, User]`, or `MutableSequence[User]`, the DTO backend raises:

```
RuntimeError: Unexpected origin type 'Sequence', expected collection type
```

The same annotation works correctly with concrete collection types like `list[User]` or `dict[str, User]`.

This prevents the use of interface-oriented type hints (e.g., `Sequence` instead of `list`) in places where DTO processing is involved.

**Expected behavior:**
- Generic collection types that are ABCs (both from `typing` and `collections.abc`) should be handled by the DTO machinery.
- During data transfer, the backend should instantiate an appropriate concrete collection that satisfies the abstract type (e.g., `list` for `Sequence`, `dict` for `Mapping`, `set` for `Set`).
- The concrete type used must be an `InstantiableCollection`, ensuring it can be populated by the DTO transfer logic.
- This should work for all common generic collection annotations, including `AbstractSet`, `DefaultDict`, `Deque`, `Dict`, `FrozenSet`, `List`, `Mapping`, `MutableMapping`, `MutableSequence`, `MutableSet`, `Sequence`, `Set`, `Tuple`, and their `collections.abc` equivalents.

**Steps to reproduce (minimal example):**
```python
from dataclasses import dataclass
from typing import Sequence
from litestar import post
from litestar.dto.factory.stdlib.dataclass import DataclassDTO

@dataclass
class User:
    name: str
    age: int

@post(dto=DataclassDTO[User], signature_namespace={"User": User})
def handler(data: Sequence[User]) -> Sequence[User]:
    return data
```
Send a POST with `[{"name": "John", "age": 42}]`. The handler should return the same data, but instead the server raises a `RuntimeError`.

**Impact:**
- Users cannot leverage abstract collection types as idiomatic type annotations in DTO-enabled endpoints.
- Encourages hard-coding concrete types, which contradicts common Python interface practices and reduces code flexibility.

Support transfer to generic collection types. (#1764)


**Title:** `StructLoggingConfig.processors` should accept a `list` instead of an arbitrary `Iterable`

**Background**
The `processors` field on `StructLoggingConfig` is currently annotated as `Optional[Iterable[Processor]]`, and the default factory `default_structlog_processors` may return an iterable that cannot be safely reused. Structlog expects to iterate over the processors for every log call, so a single-use iterable (e.g. a generator) will break silently on subsequent calls.

**Request**

- Change the type hint of `StructLoggingConfig.processors` (and the return type of `default_structlog_processors`) to `Optional[List[Processor]]`.
- Ensure the default factory always returns a proper list, not any other iterable.
- The change must not alter the logging behaviour when no custom processors are supplied (existing defaults should still apply).
- Verify that a configuration like `StructLoggingConfig(processors=[JSONRenderer()])` performs correctly across multiple log calls – no missing processors, no runtime exceptions.

**Acceptance criteria**
- Multiple `logger.info(...)` calls with a custom processor list work identically to the first call.
- The import of `Iterable` is removed from the module if it becomes unused.
- Type checkers see a `List` rather than an `Iterable`.

Swap StructLoggingConfing.processor type hint to List (#884)


**Title:** Raise an error when accessing `Starlite.openapi_schema` without an OpenAPI configuration

**Background**
- `Starlite.openapi_schema` currently returns `OpenAPI | None`. When no `OpenAPIConfig` is given to the app, the property returns `None`.
- Several endpoints in `OpenAPIController` duplicate guard logic: they check `request.app.openapi_config` or `request.app.openapi_schema` and manually raise `ImproperlyConfiguredException` with a local message constant (`MSG_OPENAPI_NOT_INITIALIZED`).
- This pattern forces every consumer to handle `None`, produces scattered error messages, and adds `type: ignore[union-attr]` comments in tests and internal usage.

**Desired behavior**
Make `Starlite.openapi_schema` immediately raise `ImproperlyConfiguredException` if `openapi_config` is `None`. The property’s return type should become `OpenAPI` (non-optional).
All the redundant `None`-checks and manual `raise` calls in the OpenAPI controller’s route handlers (`retrieve_schema_yaml`, `retrieve_schema_json`, `root`, `swagger_ui`, `stoplight_elements`, `redoc`) should be removed; they can rely on the property raising when needed.

**Acceptance criteria**
- Accessing `app.openapi_schema` on an app where `openapi_config=None` raises `ImproperlyConfiguredException`.
- The property is typed as `OpenAPI` rather than `OpenAPI | None`.
- The OpenAPI controller no longer contains duplicate `ImproperlyConfiguredException` raises or the local `MSG_OPENAPI_NOT_INITIALIZED` constant.
- A single error message string is defined in `starlite.constants` (e.g., `OPENAPI_NOT_INITIALIZED`) and reused by the property.
- Any `type: ignore[union-attr]` annotations that were only necessary because of the old `Optional` return are removed.

refactor: Raise on access to `Starlite.openapi_schema` property. (#1397)