# v0.5.0 (26 October 2026) {#v050-26-october-2026}

[v0.5.0 on GitHub](https://github.com/higher-kinded-j/higher-kinded-j/releases/tag/v0.5.0)

This release makes the optics generated for a record easier to reach and safer to hold. Navigators are on by default, so `OrderFocus.lines().quantity()` chains through a list from a bare `@GenerateFocus`, and every optic and Focus path takes a `@Nullable` focus. The core types now refuse null as a success, as an error and as a lazy value, so a `null` fails where it is made rather than where it is read. Resources are released exactly once, whatever stops the work. Every API deprecated for removal in 0.5.0 is gone, and `MigrateDeprecationsTo0_5_0` migrates most calls.

~~~admonish info title="At a glance"
- **Navigators are on by default, and a navigator is a path.** `UserFocus.address().street()` and `OrderFocus.lines().quantity()` work with a bare `@GenerateFocus`, and the navigator goes wherever a path goes.
- **Optics over a nullable field pass a nullness checker.** Every optic and Focus path type takes a `@Nullable` focus, and a bridge into an Effect Path can say what a null focus becomes.
- **No success, error or lazy value is null.** `Either`, `Try`, `IO`, `VTask`, `VStream` and every Path over them refuse null as a success, an error or a lazy value, and `Unit` stands for a step with nothing to return.
- **Resources are released exactly once.** A `VStream`, a `Resource`, a `bracket` and a `Scope` clean up whatever stops them: an early `take`, a failure, a timeout or a cancellation.
- **Optics cost less.** A read through a traversal no longer rebuilds what it reads, a fold stops at its answer, and a Focus path rebuilds a 100,000-element list in about a millisecond.
- **Every API deprecated for removal in 0.5.0 is gone.** `MigrateDeprecationsTo0_5_0` migrates most calls, and the build table gives the fix for the rest. A Focus path's untyped shortcuts are deprecated for removal in 0.6.0, and `MigrateDeprecationsTo0_6_0` replaces them.
- **The Optics chapter is rewritten.** It reads in order from a first nested update to a PATCH, on one order service, with every example compiled and checked.
- **Some changes alter what a running program does.** Many others stop the build, so read [Upgrading from 0.4.11](#upgrading) first.
~~~

---

## Mapping {#mapping}

### Generic specs {#generic-specs}

- **A bounded generic spec covers only what its bound admits** ([#1118](https://github.com/higher-kinded-j/higher-kinded-j/issues/1118)). A spec over `Page<T>` with `T extends Number` covered a `Page<String>` field and broke the generated code. That field now reports no usable source, naming the bound, or nests through a spec for `Page<String>` declared beside it. See [A bounded spec covers only what its bounds admit](../mapping/rules.md#generic-spec-bounds).
- **A generic spec over an array of its parameter leaves a primitive array alone** ([#1091](https://github.com/higher-kinded-j/higher-kinded-j/issues/1091)). No type argument is a primitive, so a spec over `Page<T[]>` no longer covers a `Page<int[]>` field, which crashed the processor or failed in generated code. The field reports no usable source instead. See [The boundaries of a generic spec](../mapping/rules.md#generic-boundaries).
- **An element-mapped spec nests where a type argument passes through unconverted** ([#1122](https://github.com/higher-kinded-j/higher-kinded-j/issues/1122)). A spec over `TaggedPage<T, X>` whose leaf converts only the elements broke the generated code wherever it nested, since no leaf names `X`. The generated call now spells out its type arguments, keeping a `@Nullable` the records declare. See [How an element-mapped spec nests](../mapping/rules.md#element-mapped-nesting).
- **A merge fills a component through a generic spec** ([#1123](https://github.com/higher-kinded-j/higher-kinded-j/issues/1123)). A threaded spec such as `PageMapping<T>` now fills a merge as it nests in a mapping. An element-mapped spec is not supported yet, and the merge says so, offering the leaf that builds it. See [How a merge fills](../mapping/rules.md#how-a-merge-fills).
- **The most specific of several specs for a pair maps it** ([#1123](https://github.com/higher-kinded-j/higher-kinded-j/issues/1123)). `TagPageMapping` beside `PageMapping<T>`, or `PageMapping<T>` beside `CodecPageMapping<T, TDto>`, made a field ambiguous. The more specific now maps it, and a compiler note names a dependency's spec it displaced. See [The most specific spec for a pair is used](../mapping/rules.md#generic-spec-precedence).

### Beans, builders and codecs {#beans-builders-codecs}

- **More Lombok `@Singular` collections map** ([#1110](https://github.com/higher-kinded-j/higher-kinded-j/issues/1110)). A build-only collection whose name has a capital second letter, such as `eMails`, is written whole, whichever capitalisation Lombok gives its clear method. So is one of an unbounded wildcard, or a Guava `ImmutableTable`. An `Iterable` property without type arguments is no longer taken for one. See [A Lombok `@Singular` collection](../mapping/rules.md#singular-collections).
- **A method that only starts like an accessor is never read as one** ([#1128](https://github.com/higher-kinded-j/higher-kinded-j/issues/1128)). A name that runs on in lower case after its `get`, `is` or `set` prefix is a word, while `geteMail()` stays the JavaBeans getter of `eMail`. So `getaway()` no longer reads `away`, and a builder's `settings(String)` writes `settings`. See [How a bean is read and written](../mapping/rules.md#how-a-bean-is-read-and-written).
- **A word a component needs is refused, with the accessor to write** ([#1128](https://github.com/higher-kinded-j/higher-kinded-j/issues/1128)). A bean's `getx()` and `setx(int)`, with no accessor of `x` beside them, used to be read as `x`. The error now offers `getX()` and `setX(int)`, or an `@Unmapped` marker. See [A method that only starts like an accessor](../mapping/rules.md#word-accessors).
- **A builder with both `name(...)` and `setName(...)` maps** ([#1128](https://github.com/higher-kinded-j/higher-kinded-j/issues/1128)): the two were taken for rival overloads, which javac never weighs against each other, and the property was refused. `name(...)` writes it. See [How a bean is read and written](../mapping/rules.md#how-a-bean-is-read-and-written).
- **The processor names what an accessor-style leaf or marker meant** ([#907](https://github.com/higher-kinded-j/higher-kinded-j/issues/907)): a leaf written `getEmail()` for the component `email` now draws `Did you mean 'email()'?`, as a near misspelling already did. An `@Unmapped` or `@ReadOnly` marker named after its accessor, `setStamp()` or `getId()`, draws the property the same way. See [Compiler Messages](../mapping/compiler_errors.md#leaf-names-no-component).
- **A date codec refuses a formatter that cannot parse what it writes** ([#1144](https://github.com/higher-kinded-j/higher-kinded-j/issues/1144)): `StandardCodecs.localDate(formatter)` and `offsetDateTime(formatter)` throw `IllegalArgumentException` when built over one, such as a pattern with no year. Such a codec used to reject every input while quoting a sample it rejected itself. See [Canonical forms only](../mapping/codecs.md#canonical-forms-only).
- **A misspelt `@MapKey` value suggests the component's bare name** ([#1112](https://github.com/higher-kinded-j/higher-kinded-j/issues/1112)). The hint now offers `byOwner` rather than `byOwner()`, so it can be used as the annotation's value. Hints that name methods keep their parentheses.

---

## Optics {#optics}

### Navigators by default {#navigators}

- **`@GenerateFocus` generates navigators by default, and a navigator is a path** ([#1033](https://github.com/higher-kinded-j/higher-kinded-j/issues/1033)). `UserFocus.address().street()` works on a bare annotation, and `generateNavigators = false` turns navigators off. A navigator can be held as a `FocusPath` or passed to an Effect Path's `focus`, and every path method works on it. See [Fluent navigation with generated navigators](../optics/focus_navigation.md#fluent-navigation-with-generated-navigators).
- **`OrderFocus.lines().quantity()` chains with no `.via(...)`** ([#1103](https://github.com/higher-kinded-j/higher-kinded-j/issues/1103)). A `List`, `Set`, `Collection`, `Optional` or `Maybe` of records gets a navigator, and so does one nested in another, such as `List<Optional<LineItem>>`, or a `Kind` the processor traverses. The navigator is the path the method returned before, so code that declares that path type still compiles. See [Which fields get a navigator](../optics/focus_navigation.md#which-fields-get-a-navigator).
- **`OrderHistoryFocus.orders().customer().email()` keeps navigating after a hop that widens the path** ([#1214](https://github.com/higher-kinded-j/higher-kinded-j/issues/1214)). Each navigator nests a form for each wider path type, `AffineForm` and `TraversalForm`, and a widening hop into a record field returns it. A record that writes `generateNavigators = true` is told when a form is left out. See [Which fields get a navigator](../optics/focus_navigation.md#which-fields-get-a-navigator).
- **A navigator steps into a container only where the record widens it** ([#1033](https://github.com/higher-kinded-j/higher-kinded-j/issues/1033)). A `Map`, an array or a third-party collection of `@GenerateFocus` records gets a navigator under `widenCollections = true`, or when the new `widenFields` names the field. So turning navigators on never changes what a field's method focuses on. See [The `ZERO_OR_MORE` Asymmetry](../optics/focus_containers.md#the-zero_or_more-asymmetry-and-widencollections).
- **A navigator reaches into a generic record** ([#739](https://github.com/higher-kinded-j/higher-kinded-j/issues/739), [#1061](https://github.com/higher-kinded-j/higher-kinded-j/issues/1061)): a field such as `Revision<EmailAddress> email` gets a navigator instantiated with its type arguments, so `ContactHistoryFocus.email().current()` chains. Where a field gets no navigator, as one given a wildcard does, its note names the path its method returns. See [A target with type parameters](../optics/focus_navigation.md#a-target-with-type-parameters).
- **A field named like a path method is handled at the declaration** ([#1033](https://github.com/higher-kinded-j/higher-kinded-j/issues/1033)). A navigator never reaches a field such as `last`, since `name().last()` would call `FocusPath.last()`. The field reaching such a record keeps its plain path, with a note, or is refused under `generateNavigators = true`. Fields named `get`, `set` and `count` navigate. See [A field named like a path method](../optics/focus_navigation.md#a-field-named-like-a-path-method).
- **Fields such as `items` and `Items` keep one navigator between them** ([#1259](https://github.com/higher-kinded-j/higher-kinded-j/issues/1259)). Where both would have a navigator, their classes would share a name, so the first in declaration order keeps it. The later one keeps its plain path, with a note, or is refused where the record writes `generateNavigators = true`. See [Two fields with one navigator name](../optics/focus_navigation.md#two-fields-with-one-navigator-name).
- **`@GenerateFocus` checks the names in `includeFields`, `excludeFields` and `widenFields`** ([#1104](https://github.com/higher-kinded-j/higher-kinded-j/issues/1104)). A name that matches no component is an error at the value, offering the component meant, such as `positions` for `"postions"`. A list the record never reads warns, as does a `widenFields` name whose field cannot widen. See [Compiler Messages](../optics/compiler_errors.md#generatefocus-field-list-unknown-name).
- **A `@GenerateFocus` setting that names a constant javac cannot resolve leaves only javac's error** ([#1104](https://github.com/higher-kinded-j/higher-kinded-j/issues/1104)), such as `includeFields = Names.MISSING`. The processor stopped with an exception, which hid that error.
- **`@GenerateFocus` warns of an `includeFields` name whose field has no navigator** ([#1181](https://github.com/higher-kinded-j/higher-kinded-j/issues/1181), [#1201](https://github.com/higher-kinded-j/higher-kinded-j/issues/1201), [#1207](https://github.com/higher-kinded-j/higher-kinded-j/issues/1207)). Naming only a `String` field, say, left every field without a navigator, and nothing said why. The warning says why and offers a fix: another field to name, a record to annotate, a container to widen, or `includeFields` to remove, with navigators off. See [Compiler Messages](../optics/compiler_errors.md#generatefocus-includefields-no-navigator).
- **`@GenerateFocus` reports a `TraversableGenerator` priority tie once** ([#1081](https://github.com/higher-kinded-j/higher-kinded-j/issues/1081)): the warning lands on the component that reaches the tied type, however many navigators reach it. It used to repeat for each one. See [Compiler Messages](../optics/compiler_errors.md#multiple-traversablegenerator-spi-providers-with-equal-priority-n-support-type-x-a-warning).

### Optics and null {#optics-and-null}

- **Optics over a nullable field pass a nullness checker** ([#767](https://github.com/higher-kinded-j/higher-kinded-j/issues/767), [#1017](https://github.com/higher-kinded-j/higher-kinded-j/issues/1017)): every optic and Focus path type, and `Kind`, takes a nullable type, so the lens generated for a `@Nullable` component checks under JSpecify. A bridge into an effect type can say what a null focus becomes, as `toEitherPath(source, error)` does. See [A focus that may be null](../optics/focus_effects.md#a-focus-that-may-be-null).
- **`nullable()` keeps the focus type** ([#1017](https://github.com/higher-kinded-j/higher-kinded-j/issues/1017)): `FocusPath.nullable()` and `AffinePath.nullable()` return the focus type without its null, so a chained `focus(path.nullable(), error)` or a `var` infers it where it inferred `Object`. A call written `path.<String>nullable()` still compiles. See [`.nullable()`: Handle Null Values](../optics/focus_navigation.md#nullable-handle-null-values).
- **A Focus path passes over a null it would step into** ([#1062](https://github.com/higher-kinded-j/higher-kinded-j/issues/1062)). A `@Nullable` `List`, `Optional`, `Either` or other container now widens through `.nullable()` first, so its path is empty when the container is null, where it threw `NullPointerException`. A navigator into `Map<String, @Nullable Address>` reads the other elements and leaves a null one in place. See [Where a `@Nullable` counts](../optics/focus_navigation.md#where-a-nullable-counts).
- **`modifyAllEither`, `modifyAllValidated` and `modifyAllMaybe` keep a null element their traversal passes over** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): over `filtered` or a prism that leaves a null element alone, `modifyAllValidated` threw and `modifyAllMaybe` gave `Nothing`. Each now keeps the null, and still rebuilds nothing once a step fails. So do `OpticOps`' validating modifiers. See [`modifyAllEither`: First Error Only](../optics/optics_extensions.md#modifyalleither-first-error-only).

### The containers a Focus path widens {#focus-containers}

- **A `Maybe` component's Focus path reads and writes its `Just` value** ([#757](https://github.com/higher-kinded-j/higher-kinded-j/issues/757)): `@GenerateFocus` now widens a `Maybe` field, and one nested in another container such as `List<Maybe<A>>`, through `.some(Affines.just())`. It used the `Optional`-only `.some()`, so the first read or write threw `ClassCastException`. See [Supported Container Types](../optics/focus_containers.md#supported-container-types).
- **A raw or wildcard `Maybe`, `Set` or `Collection` component widens, as a `List` does** ([#1251](https://github.com/higher-kinded-j/higher-kinded-j/issues/1251)). `Set<? extends Widget>` widens to a `TraversalPath` onto `Widget`. In 0.4.11 such a `Set` or `Collection` was rejected at the declaration, and such a `Maybe` threw `ClassCastException` on first use. See [Supported Container Types](../optics/focus_containers.md#supported-container-types).
- **`@GenerateFocus` widens an array, as the Focus pages say** ([#1062](https://github.com/higher-kinded-j/higher-kinded-j/issues/1062)). Under `widenCollections = true`, a `String[]` or other array of a reference type is a `TraversalPath` over its elements, and an array of `@GenerateFocus` records gets a navigator over them. A primitive array stays a plain path. See [Supported Container Types](../optics/focus_containers.md#supported-container-types).
- **A Focus path through a custom container whose generator names no optic stops at the container** ([#1059](https://github.com/higher-kinded-j/higher-kinded-j/issues/1059)), as if no generator supported it. It used to widen through the `Optional`-only `.some()` or the `List`-only `.each()`, and threw `ClassCastException` on first use for other containers. The generator still serves `@GenerateTraversals` and `@ImportOptics`. See [Registering your own container type](../optics/focus_containers.md#the-fine-print-registering-your-own-container-type).
- **A Focus path's untyped shortcuts are deprecated for removal in 0.6.0** ([#1105](https://github.com/higher-kinded-j/higher-kinded-j/issues/1105)). `each()`, `some()`, `at(index)` and the others compile on any focus, then throw `ClassCastException` on the wrong one. Each typed form, such as `each(EachInstances.listEach())`, reads the element type from the path and does not compile on another focus. See [Deprecated for removal in 0.6.0](#deprecated).
- **`EachIndexed` is the one way to an indexed traversal from an `Each`** ([#1261](https://github.com/higher-kinded-j/higher-kinded-j/issues/1261)): `Each.eachWithIndex()`, deprecated since 0.4.7, is removed. It let the caller choose the index type, so a wrong one compiled, then threw `ClassCastException`. `indexedTraversal()` fixes the index type in its signature. See [Using Indexed Traversal](../optics/each_typeclass.md#using-indexed-traversal).

### Composition and cost {#composition-and-cost}

- **Any two of `Iso`, `Lens`, `Prism`, `Affine` and `Traversal` compose directly with `andThen`** ([#1021](https://github.com/higher-kinded-j/higher-kinded-j/pull/1021)). An `Iso` followed by a `Traversal` now returns a `Traversal`, and so does a `Traversal` followed by an `Affine` or an `Iso`, with no `asTraversal()` first. The composition table is read from the overloads, so it always matches the library. See [Composition Rules Table](../optics/composition_rules.md#composition-rules-table).
- **A read through a traversal no longer rebuilds what it reads** ([#1056](https://github.com/higher-kinded-j/higher-kinded-j/pull/1056)). `Traversals.getAll`, a `TraversalPath`'s reads, the indexed reads and `IxedInstances.get` read each focus as `asFold()` does, so reading a list or map through an optic no longer builds a new one. A prism or affine that matches builds nothing it throws away. See [What reads cost](../optics/production_readiness.md#read-cost).
- **A fold's query stops at its answer** ([#1056](https://github.com/higher-kinded-j/higher-kinded-j/pull/1056)). `preview`, `find`, `exists`, `all` and `isEmpty` on `Fold`, `TraversalPath` and `IndexedFold` visit no focus after the one that settles them, so `exists` over a long list returns at its first match. A path from `traced` still reads every focus, so its observer sees them all. See [What reads cost](../optics/production_readiness.md#read-cost).
- **A Focus path steps into a `List` in time proportional to the list** ([#1063](https://github.com/higher-kinded-j/higher-kinded-j/issues/1063)). Its `.each(EachInstances.listEach())`, which every generated `List` path uses, now rebuilds the list once instead of copying it at every element. On 100,000 elements that took several seconds, and now takes about a millisecond. `EachInstances.arrayEach()` rebuilds an array the same way. See [Traversal allocation](../optics/production_readiness.md#traversal-allocation).
- **A map written through an optic keeps its source's order** ([#1022](https://github.com/higher-kinded-j/higher-kinded-j/pull/1022)): every optic that writes a `Map`, from `Traversals.forMapValues` to `AtInstances.mapAt` and `Setter.forMapValues`, hands back a map in the source's iteration order. An updated key keeps its place and a new key goes at the end. See [Persistent and Third-Party Maps](../optics/common_data_structure_traversals.md#persistent-and-third-party-maps-formapvaluescollecting).
- **`modifyWhen` and `branch` on a `Traversal` call only the function their predicate picks** ([#1026](https://github.com/higher-kinded-j/higher-kinded-j/pull/1026)). Each element is now tested first, so a function may assume the condition that guards it, and an expensive one runs only where it is needed. `Lens.modifyWhen`, `modifyBranch` and `setIf` test first too. See [Complete, Runnable Example](../optics/composing_optics.md#complete-runnable-example).
- **`Setter.forList` and `Setter.forMapValues` give the same result on every run** ([#1018](https://github.com/higher-kinded-j/higher-kinded-j/issues/1018)): each time the effect from `modifyF` runs, it builds its own list or map. So an `IO` gives an equal value on every run, and the `List` applicative gives every combination of choices. `Traversals.traverseMapValues` does the same for an empty map. See [Sequencing Effects in Collections](../optics/setters.md#sequencing-effects-in-collections).

### Copy strategies, Lombok and protobuf builders {#copy-strategies}

- **`@ViaBuilder` rebuilds through Lombok's `@SuperBuilder(toBuilder = true)`** ([#1110](https://github.com/higher-kinded-j/higher-kinded-j/issues/1110)). Its `toBuilder()` hands back a builder typed with wildcards, `PetBuilder<?, ?>`, whose setter and `build()` the processor now follows as javac does. So does a hand-written builder handed back with wildcards, where a step returns one of its own type parameters. See [Non-Standard Naming](../optics/copy_strategies.md#non-standard-naming).
- **More `@ViaBuilder` chains are checked at the spec** ([#1110](https://github.com/higher-kinded-j/higher-kinded-j/issues/1110)). A spec over `Node<?>` whose `toBuilder()` returns a type parameter of `Node` builds. A builder bounded by more than one type is checked too, and a chain that cannot compile is refused at the spec rather than in the generated file. See [the chain the lens rebuilds through](../optics/compiler_errors.md#viabuilder-the-chain-the-lens-rebuilds-through).
- **A `@ViaBuilder` lens replaces a collection its builder would add to** ([#1110](https://github.com/higher-kinded-j/higher-kinded-j/issues/1110)). For a Lombok `@Singular List<String> tags`, or a protobuf-java message's `addAllTags` or `putAllPoints`, the generated lens first calls the `clearTags()` or `clearPoints()` written beside it, so the lens laws hold. That covers `@Builder` and `@SuperBuilder`, with or without a lower-case `setterPrefix`. See [Non-Standard Naming](../optics/copy_strategies.md#non-standard-naming).
- **A `@Wither` that returns its class's own type parameter works on a source written with a wildcard** ([#1110](https://github.com/higher-kinded-j/higher-kinded-j/issues/1110)): `S withName(String)` on a `Shape<S extends Shape<S>>` rebuilds a `Shape<?>`. See [Copy Strategies](../optics/copy_strategies.md#wither-types-with-withx-methods).
- **A spec's diagnostics never offer a deprecated method** ([#1110](https://github.com/higher-kinded-j/higher-kinded-j/issues/1110)). Following one failed a build under `-Werror` in the generated file. A lens named after a protobuf repeated or map field is offered `getTagsList()` or `getPointsMap()`, never the deprecated `getPoints()`. See [the missing accessor](../optics/compiler_errors.md#-has-no-method--for-the-generated-lens-to-read-the-value-it-focuses).
- **The processor suggests methods spelt the way each copy strategy calls them** ([#907](https://github.com/higher-kinded-j/higher-kinded-j/issues/907)). A getter written `name` against a bean's `getName()` now draws `Did you mean 'getName'?`, and `getActive` draws `'isActive'`. A setter or wither is offered in the convention its strategy needs: a builder's `setName` or `name`, a bean's `setName`, `@Wither`'s `withName`. See [Compiler Messages](../optics/compiler_errors.md#-has-no-method--for-the-generated-lens-to-read-the-value-it-focuses).
- **`@ViaConstructor` holds its order to the constructor** ([#906](https://github.com/higher-kinded-j/higher-kinded-j/issues/906)). An order that passes a value where the constructor's parameter names say another belongs, or that no constructor takes, is now refused at the spec method. Leave `parameterOrder` out and it is read from those names. See [`@ViaConstructor`: constructor-only value types](../optics/copy_strategies.md#viaconstructor-constructor-only-value-types).
- **A `@ViaConstructor` order that leaves a value out says so** ([#1067](https://github.com/higher-kinded-j/higher-kinded-j/issues/1067)). A written order that rebuilds through a shorter constructor now warns, naming the values longer constructors take. `@SuppressWarnings("shorter-constructor")` keeps quiet a constructor that works the value out on purpose. A variable-arity constructor is held to its parameter names too. See [the warning's entry](../optics/compiler_errors.md#viaconstructor-shorter-constructor).
- **A `@ViaCopyAndSet` lens keeps what its `withX` setter returns** ([#1108](https://github.com/higher-kinded-j/higher-kinded-j/issues/1108)). A `withTitle` setter had its new value dropped. The lens now hands back what it returns where every method of that name the new value can be passed to returns the source type. One it cannot keep warns, unless the lens method is annotated `@SuppressWarnings("dropped-result")`. See [`@ViaCopyAndSet`](../optics/copy_strategies.md#viacopyandset-legacy-types-with-a-copy-constructor-and-setters).
- **`@ImportOptics` takes no word for a wither** ([#1128](https://github.com/higher-kinded-j/higher-kinded-j/issues/1128)). A name that runs on in lower case after `with` is a word, so a `withdraw(long)` beside `getDraw()`, which gave a `draw` lens that added to `draw` rather than setting it, now gives none. A `with2D(int)` beside `get2D()` no longer stops the processor. See [Wither Classes to Lenses](../optics/importing_optics.md#wither-classes-to-lenses).
- **A spec lens that would read or rebuild unchecked through a raw type is refused at the spec method** ([#905](https://github.com/higher-kinded-j/higher-kinded-j/issues/905)). A raw accessor read as `List<String>`, or a setter called through a raw builder, left an unchecked operation in generated code, which failed a build under `-Xlint:unchecked -Werror`. The error names the member at fault. See [Compiler Errors](../optics/compiler_errors.md#spec-unchecked-conversion).

### Prisms and `@InstanceOf` {#prisms}

- **A prism onto an inner class of a generic class compiles under `-Werror`** ([#886](https://github.com/higher-kinded-j/higher-kinded-j/issues/886)): a `Dot` declared inside `class Outlines<X>` gets `Prism<Outlines.Outline, Outlines<?>.Dot>`, from `@GeneratePrisms`, `@ImportOptics` and an `@InstanceOf` spec method. A generic inner class gets a prism too, where `@InstanceOf` refused one. See [Generating the Prisms](../optics/prisms.md#step-1-generating-the-prisms) and [Parameterised Targets](../optics/optics_spec_interfaces.md#parameterised-targets).
- **`@InstanceOf` reports a source argument outside the target's bound at the spec method** ([#1082](https://github.com/higher-kinded-j/higher-kinded-j/issues/1082)). A `Gauge<Y extends Number>` reached from a `Node<U>` whose `U` could be anything used to fail in the generated file, where javac refused `Gauge<U>`. Where one bound would do, the message gives the declaration to write. See [Parameterised Targets](../optics/optics_spec_interfaces.md#parameterised-targets).
- **`@InstanceOf` refuses a test that could never match** ([#1091](https://github.com/higher-kinded-j/higher-kinded-j/issues/1091)). A `Grid<Y> extends Node<Y[]>` reached from a `Node<int[]>` crashed the processor, and is now refused at the spec method. Where the test earns no source type, as a `Box<?>` is no `Node<U>`, the message says no focus would do and offers another class or a spec. See [the message's entry](../optics/compiler_errors.md#instanceof-never-matches).
- **A method named after a Java keyword takes a trailing underscore** ([#1056](https://github.com/higher-kinded-j/higher-kinded-j/pull/1056)). An enum constant such as `NEW` or `TRUE`, a sealed subtype named `Default`, an `@EffectAlgebra` permit named `Throw`, or an imported `withDefault` crashed the processor. Their methods are now `new_()`, `true_()`, `default_()` and `throw_()`, and so are `@GenerateErrorEnvelope` factories. See [Prisms](../optics/prisms.md).

---

## Effect Paths {#effect-paths}

### Values that are never null {#never-null}

- **A `Right` or a `Success` always holds a value** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): `Either.right`, `Try.success`, their Path, `EitherT` and `ErrorContext` factories, and `of` on their monads, refuse null, as `Validated` and `EitherOrBoth` already did. A step that succeeds with nothing to return gives `Unit.INSTANCE`, as `VTask.exec` and `Path.ioRunnable` already do. See [Creating Instances](../monads/either_monad.md#creating-instances).
- **A `Left` always holds an error** ([#1077](https://github.com/higher-kinded-j/higher-kinded-j/issues/1077)): `Either.left`, `EitherT.left`, `raiseError` on `EitherMonad` and `EitherTMonad`, and `Maybe.toEither` refuse null, as every Effect Path has done since 0.4.11. An error with no detail to carry is `Unit.INSTANCE`. See [Creating Instances](../monads/either_monad.md#creating-instances).
- **A lazy computation always gives a value** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `IO`, `VTask`, `VStream`, `State`, `Lazy`, every other lazy type and their Paths refuse a null value. A value passed in throws at once, and a function that returns null fails the computation when it runs. Only `Id` keeps a null, for the optics. See [A Task Always Gives a Value](../monads/vtask_monad.md#a-task-always-gives-a-value).
- **Every `raiseError` refuses a null error** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `MonadError.raiseError` takes an error that is never null, so a nullness checker flags `raiseError(null)` on any instance. `Maybe`, `Optional`, `MaybeT` and `OptionalT`, whose failure carries no detail, take `Unit.INSTANCE`. See [The Solution: `raiseError` and `handleErrorWith`](../functional/monad_error.md#the-solution-raiseerror-and-handleerrorwith).
- **`bracketOutcome`'s release sees its error as a `Maybe`** ([#1077](https://github.com/higher-kinded-j/higher-kinded-j/issues/1077)): release takes an `Either<Maybe<E>, B>`. A typed failure arrives as `Left(Just(e))`, and a failed `onDefect` as `Left(Nothing)`, where it was `Left(null)`. A release lambda that never reads the error needs no change. See [Outcome-aware structured concurrency](../effect/path_vresult.md#outcome-aware-structured-concurrency).
- **The Path SPI answers with an `Optional` where it has nothing to give** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `PathProvider.monadError()` and `PathRegistry.unregister` return an `Optional`, as `PathRegistry.getProvider` and `GenericPath.monadError()` do, where they returned null. See [PathProvider SPI Registration](../monads/vstream_advanced.md#pathprovider-spi-registration).
- **A deferred trampoline step names a supplier that returns null** ([#1153](https://github.com/higher-kinded-j/higher-kinded-j/issues/1153), [#1161](https://github.com/higher-kinded-j/higher-kinded-j/issues/1161)): `Trampoline.defer`, `TrampolinePath.defer` and `Path.trampolineDefer` report `supplier must not return null` when that step runs. The supplier stays lazy.

### Removed after their deprecation {#removed}

- **`StateT` is built from its state function alone** ([#445](https://github.com/higher-kinded-j/higher-kinded-j/issues/445)): its constructor, `create` and `StateTKindHelper.stateT` take no `Monad`, and `mapT` takes only the transformation, as on every other transformer. The record holds only that function, so `equals`, `hashCode` and `toString` leave the `Monad` out. See [Running StateT Computations](../transformers/statet_transformer.md#running-statet-computations).
- **`Try` and `TryPath` fold failure first, as `Either` does** ([#452](https://github.com/higher-kinded-j/higher-kinded-j/issues/452)): the success-first `fold`, deprecated since 0.4.6, is removed, leaving `foldFailureFirst`. A call to `fold` fails to compile rather than running its two functions the wrong way round. See [Handling Failures](../monads/try_monad.md#handling-failures-foldfailurefirst-recover-recoverwith).
- **Each narrow on `StateTKind` and `KindValidator` has one name** ([#1260](https://github.com/higher-kinded-j/higher-kinded-j/issues/1260)): `narrowK`, deprecated in 0.4.5, and `narrowWithPattern`, deprecated in 0.4.4, are removed. `StateTKind.narrow` checks the witness at compile time, which `narrowK`'s `Kind<?, A>` parameter did not. `KindValidator.narrowHolder` takes what `narrowWithPattern` took.
- **`@PathConfig` and the `EFFECTFUL` and `ACCUMULATING` capabilities are removed** ([#1262](https://github.com/higher-kinded-j/higher-kinded-j/issues/1262)): all three were deprecated in 0.4.11 because none did what it said. No processor read `@PathConfig`, and the two capabilities generated what `CHAINABLE` and `RECOVERABLE` do, so nothing generated changes. To rename a Path class, set `suffix` on `@PathSource`. See [Naming and placement](../effect/path_source.md#naming-and-placement).

### Resources, cancellation and concurrency {#resources-and-cancellation}

- **A `VStream` releases its resources when it stops early or fails** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024)): closing reaches every finaliser upstream that the stream has started reading, through any operator, innermost first. `takeWhile`, `zipWith`, `headOption`, `find` and a failing terminal operation close what they leave unread, so `bracket(...).map(f).headOption()` releases at once. `VStream.closeAfterFailure` closes a failed manual pull. See [onFinalize: Lightweight Cleanup](../monads/vstream_resources.md#onfinalize-lightweight-cleanup).
- **`VStreamReactive` passes cancellation across the bridge** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024)): a `toPublisher` subscription closes the rest of its stream when it cancels or the stream fails, once it has requested elements. Closing a `fromPublisher` stream cancels its subscription, so `take(n)` stops the publisher. See [Reactive Interop: Flow.Publisher Bridge](../monads/vstream_advanced.md#reactive-interop-flowpublisher-bridge).
- **One `Resource` can serve overlapping uses** ([#1019](https://github.com/higher-kinded-j/higher-kinded-j/issues/1019)): each `use` releases exactly what it acquired, however the `Resource` was composed. So a `Resource` built once with `map` or `flatMap` and used nested, or once per concurrent request, no longer releases one resource twice and leaks the other. See [Using One Resource Many Times](../monads/vtask_resource.md#using-one-resource-many-times).
- **`bracket`, `guarantee` and `withResource` report a failing cleanup as try-with-resources does** ([#1053](https://github.com/higher-kinded-j/higher-kinded-j/issues/1053)): when a use fails and its cleanup throws too, the use's exception is reported with the cleanup's suppressed onto it. An exception from `close()` is reported, so a writer whose final flush fails no longer reports success. See [The bracket Pattern](../effect/advanced_topics.md#the-bracket-pattern).
- **A release that blocks finishes after a cancellation** ([#1057](https://github.com/higher-kinded-j/higher-kinded-j/issues/1057), [#1073](https://github.com/higher-kinded-j/higher-kinded-j/issues/1073)): it runs with the interrupt status cleared, then restored. That covers a `bracketOutcome` release, a release after a use that caught a cancellation and returned, and each `Saga` compensation. A blocking release, such as a refund, used to fail at once. See [The bracket Pattern](../effect/advanced_topics.md#the-bracket-pattern) and [Saga](../resilience/saga.md#handling-compensation-failures).
- **A `Scope` can run again, be retried, or run on several threads** ([#1052](https://github.com/higher-kinded-j/higher-kinded-j/issues/1052)): each run of the `VTask` from `join()` opens its own `StructuredTaskScope` with a new `Joiner`. A second run used to see the first run's results, and a retry kept failing with the first attempt's error. `VResultPath.allSucceed` and `firstSuccess` follow. See [Running a Scope Again](../monads/vtask_scope.md#running-a-scope-again).
- **Resilience patterns keep a task's exception and its cancellation** ([#1072](https://github.com/higher-kinded-j/higher-kinded-j/issues/1072)): `Retry.retryTask`, `Bulkhead.protect` and their Path forms, and `CircuitBreaker.protectWithFallback`, no longer wrap a checked exception, so `retryOn(IOException.class)` retries a task's `IOException`. A cancelled task is not run again or counted by a circuit breaker, and a timeout passes the cancellation on. See [Handling Exhausted Retries](../resilience/retry.md#handling-exhausted-retries).

### Streams, sagas and stack safety {#streams-and-sagas}

- **A `VStream` chunk is a `NonEmptyList`** ([#580](https://github.com/higher-kinded-j/higher-kinded-j/issues/580)): `chunk`, `chunkWhile` and `mapChunked`, and `chunk` on `VStreamPath` and `VStreamContext`, hand over a `NonEmptyList`, so `head()`, `reduce` and `max` on a chunk always give a value. `NonEmptyList` gains `stream()`, so a batch function such as `chunk -> chunk.stream().map(f).toList()` compiles unchanged. See [Chunking Operations](../monads/vstream_parallel.md#chunking-operations).
- **A large chunk size or concurrency on a short stream works** ([#1238](https://github.com/higher-kinded-j/higher-kinded-j/issues/1238), [#1249](https://github.com/higher-kinded-j/higher-kinded-j/issues/1249)): `chunk(Integer.MAX_VALUE)`, or `parEvalMap` with that concurrency, ran out of memory before reading an element. Each batch list was sized to the argument, and now starts small and grows. The other chunked and parallel calls, and their `VStreamPath` and `VStreamContext` forms, follow. See [Chunking Operations](../monads/vstream_parallel.md#chunking-operations).
- **A saga builder with no steps does not compile** ([#580](https://github.com/higher-kinded-j/higher-kinded-j/issues/580)): `SagaBuilder.start()` returns a `SagaBuilder.Start`, which has the step methods but no `build()`. Each step returns a `SagaBuilder` whose `build()` never fails. An empty builder threw `IllegalStateException`. Steps that come from data are held as a `SagaSteps`. See [Steps From Data](../resilience/saga.md#steps-from-data).
- **`TrampolineUtils.traverseListStackSafe` is stack-safe under every applicative** ([#1018](https://github.com/higher-kinded-j/higher-kinded-j/issues/1018)): it combines the effects as a balanced tree, so a million-element traversal under `IO` no longer overflows the stack. It builds the result in linear time, and a `Validated` collecting its errors in a list is no longer quadratic in the failures. See [Core Components](../monads/trampoline_monad.md#core-components).
- **`@PathSource` points an effect algebra to `FreePath`** ([#1008](https://github.com/higher-kinded-j/higher-kinded-j/issues/1008)): a Path over a witness that `@EffectAlgebra` generates now draws a note at the witness. The algebra has a `Functor` and no `Monad` to pass to the Path's `of` and `pure`, so the note shows how to wrap its programs in a `FreePath`. See [A witness another processor writes](../effect/path_source.md#a-witness-another-processor-writes).

---

## Spring {#spring}

- **A streamed `VStreamPath` response closes its stream when it stops** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024)): a client disconnect, a timeout or a serialisation failure now runs the stream's finalisers. See [VStreamPath: SSE Streaming on Virtual Threads](../spring/spring_boot_integration.md#vstreampath-sse-streaming).
- **An SSE stream from a declarative HTTP client closes when you stop early** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024)): `headOption()`, `find(...)` or `take(n)` on a stream from `HkjClientExchange.vstream` now closes the response. There is no need to drain it first. See [Streaming with `VStreamPath`](../spring/declarative_http_clients.md#streaming-with-vstreampath).
- **An endpoint that sends no body is declared `Unit`** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): a declarative client method returning `EitherPath<E, Unit>`, `VTaskPath<Either<E, Unit>>` or `MaybePath<Unit>` ignores the body. A controller's `Unit` success writes no body from every handler that writes one value. See [Choosing a Return Type](../spring/declarative_http_clients.md#choosing-a-return-type).
- **`hkj-spring` is built against Spring Boot 4.1.1** ([#1177](https://github.com/higher-kinded-j/higher-kinded-j/issues/1177)): `hkj-spring-boot-autoconfigure` publishes Spring Boot's BOM as a Gradle platform. Gradle then raises the Spring Boot your application runs with, and each library it manages, to at least 4.1.1's versions; a newer requested version still wins. Spring's dependency-management plugin or a Maven BOM import keeps your version, which must be 4.1.0 or later.

---

## Testing {#testing}

- **The lens and affine law harnesses check set-set in both orders** ([#1169](https://github.com/higher-kinded-j/higher-kinded-j/issues/1169), [#1175](https://github.com/higher-kinded-j/higher-kinded-j/issues/1175)). The two focus values may be given in either order, and an affine is checked on its present target. The harness is stricter: a fixture that broke set-set only in the order left untested now fails. See [Optic Laws](../tooling/test_assertions.md#optic-laws).
- **Lens and affine law fixtures may include the current focus** ([#812](https://github.com/higher-kinded-j/higher-kinded-j/issues/812)). `assertLensLaws` and `assertAffineLaws` require only that their two focus values differ from each other. A boolean lens can use `false, true`. See [Optic Laws](../tooling/test_assertions.md#optic-laws).
- **`assertAffineLaws` law-checks an affine that writes to an absent focus** ([#1023](https://github.com/higher-kinded-j/higher-kinded-j/pull/1023)): `Affines.some()` and a `Lens.andThen(Prism)` affine now pass. On the absent target it checks that `modify` changes nothing, that `set` either changes nothing or writes a value that reads back, and set-set. Add `assertSetNoOpWhenAbsent` for an affine that must leave it alone. See [When the focus is absent](../optics/affine.md#when-the-focus-is-absent).
- **The library's own type classes are law-checked more widely** ([#1151](https://github.com/higher-kinded-j/higher-kinded-j/issues/1151), [#1203](https://github.com/higher-kinded-j/higher-kinded-j/issues/1203), [#1208](https://github.com/higher-kinded-j/higher-kinded-j/issues/1208), [#1212](https://github.com/higher-kinded-j/higher-kinded-j/issues/1212), [#1218](https://github.com/higher-kinded-j/higher-kinded-j/issues/1218), [#1220](https://github.com/higher-kinded-j/higher-kinded-j/issues/1220), [#1222](https://github.com/higher-kinded-j/higher-kinded-j/issues/1222)). The Applicative laws now run for `List`, `NonEmptyList`, `EitherOrBoth` and `Trampoline`, the MonadError laws for `CompletableFuture`, and the Bifunctor laws for `Const` over generated values. The build now fails for a `Monad` with no test class of its own.
- **Showcase tests demonstrate `EitherOrBothAssert` and `NonEmptyListAssert`** ([#1191](https://github.com/higher-kinded-j/higher-kinded-j/issues/1191)): they join the `hkj-test` examples a reader can copy. Property tests in `hkj-core` and `hkj-checker` share one set of defaults ([#1217](https://github.com/higher-kinded-j/higher-kinded-j/pull/1217)).

---

## Build and tooling {#build-and-tooling}

- **Checker severity settings and the library's formatting work under every default locale** ([#1168](https://github.com/higher-kinded-j/higher-kinded-j/issues/1168)): `severity=WARNING` and per-check `severity:<id>=WARNING` now select warnings under Turkish, where they were silently ignored. Library formatting and generated identifier case conversions use `Locale.ROOT`. The `hkj-core` and `hkj-examples` tests run under an English locale, with `Locale.ROOT` in more of their formatting ([#1160](https://github.com/higher-kinded-j/higher-kinded-j/issues/1160), [#1253](https://github.com/higher-kinded-j/higher-kinded-j/pull/1253), [#1256](https://github.com/higher-kinded-j/higher-kinded-j/pull/1256)).
- **`MigrateDeprecationsTo0_5_0` drops `StateT`'s `Monad` and widens navigated containers** ([#445](https://github.com/higher-kinded-j/higher-kinded-j/issues/445), [#1106](https://github.com/higher-kinded-j/higher-kinded-j/issues/1106)): `RemoveStateTMonadArgument` drops the `Monad` from `StateT` constructor, `create`, `stateT` and `mapT` calls. `AddWidenFieldsForNavigatedContainersRecipe` adds each container a 0.4.x navigator stepped into to `widenFields`, and marks one whose elements have a field named like a path method. See [0.5.0 deprecation migration](../tooling/openrewrite.md#050-deprecation-migration).
- **`MigrateDeprecationsTo0_5_0` marks the nulls to replace** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065), [#1077](https://github.com/higher-kinded-j/higher-kinded-j/issues/1077), [#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)). `DetectNullSuccessValuesRecipe` and `DetectNullErrorValuesRecipe` mark a `null` given as a success, a value or an error, and a lambda that returns one where it now fails. They also mark `Void` as a value or error type. They rewrite nothing, and `rewriteRun` leaves each mark as a comment. See [0.5.0 deprecation migration](../tooling/openrewrite.md#050-deprecation-migration).
- **`MigrateDeprecationsTo0_6_0` replaces a Focus path's untyped shortcuts** ([#1105](https://github.com/higher-kinded-j/higher-kinded-j/issues/1105)): the new group's `ReplaceUntypedPathShortcutsRecipe` rewrites each call to its typed form, such as `each()` to `each(EachInstances.listEach())`. It drops a needless type witness, and marks each call it cannot rewrite, such as one over a `List<? extends T>`, with its fix. See [0.6.0 deprecation migration](../tooling/openrewrite.md#060-deprecation-migration).
- **A nullness checker sees every `hkj-core` package as `@NullMarked`** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `vstream`, `constant`, `func`, `writer_t`, `effect.spi`, `util.validation` and the remaining optics packages carry the package annotation the others already did. NullAway reading `hkj-core` from the classpath ignores the module's annotation, so it treated those types as unannotated.
- **A nullness checker reads `hkj-test`'s assertions** ([#1094](https://github.com/higher-kinded-j/higher-kinded-j/issues/1094)): its packages carry `@NullMarked`, which NullAway reading the jar from the classpath needs. Every `assertThat...` entry point takes a `@Nullable` actual, as AssertJ's does, so `assertThatEither(result).isNull()` passes for a null `result`, where it threw `NullPointerException`.
- **Some `hkj-test` assertions take or give a null** ([#1094](https://github.com/higher-kinded-j/higher-kinded-j/issues/1094)): `withMessage`, `withExceptionMessage`, `whenForcedThrowsWithMessage` and `IdAssert.hasValue` take a `@Nullable` expected value. `getException()` and `getValue()` on `IOAssert` and `VTaskAssert` return `@Nullable`, since an outcome has only one of the two.
- **A generator extending the built-in `Optional`, `Maybe`, `List`, `Set` or `Collection` generator inherits no optic expression** ([#757](https://github.com/higher-kinded-j/higher-kinded-j/issues/757)): `@GenerateFocus` recognises those types by name and never asks a generator. So `generateOpticExpression()` and `getRequiredImports()` on one return the defaults, and a subclass claiming another type takes no part in `@GenerateFocus` until it names its own. See [Registering your own container type](../optics/focus_containers.md#the-fine-print-registering-your-own-container-type).
- **The `state-t-mapt-arity` check is retired** ([#445](https://github.com/higher-kinded-j/higher-kinded-j/issues/445)): every `mapT` now takes the function alone, so there is no missing argument to report. A `disable=` or `severity:` entry that names it has no effect. The `kind-value-narrow` message now names the right accessor: `run()` on `ReaderT` and `WriterT`, and `runStateT(state)` on `StateT`. See [Compile-Time Checks](../tooling/compile_checks.md).
- **`hkj-processor-plugins` no longer brings Guava and Jackson 2 onto your annotation processor path** ([#1177](https://github.com/higher-kinded-j/higher-kinded-j/issues/1177)): they came with `palantir-java-format`, which it declared but never used. The plugin guide's build snippet now names javapoet and the right Avaje SPI artefact. See [Step-by-Step Example](../tooling/generator_plugins.md#step-by-step-example).

---

## Documentation {#documentation}

### The Optics chapter {#optics-chapter}

- **The Optics chapter reads in order, from a first nested update to a PATCH** ([#1034](https://github.com/higher-kinded-j/higher-kinded-j/pull/1034), [#1038](https://github.com/higher-kinded-j/higher-kinded-j/pull/1038), [#1056](https://github.com/higher-kinded-j/higher-kinded-j/pull/1056), [#1086](https://github.com/higher-kinded-j/higher-kinded-j/pull/1086)). The sidebar starts with the pages a newcomer needs, then groups the rest by task. The examples follow one order service, the Mapping capstone's records, and every page keeps its address. See [Optics](../optics/ch_intro.md).
- **Find your field, then find your recipe** ([#1035](https://github.com/higher-kinded-j/higher-kinded-j/pull/1035), [#1045](https://github.com/higher-kinded-j/higher-kinded-j/pull/1045)). The Focus DSL page answers "what does my field give me?" with a table keyed by the field you hold. The Optics Cookbook opens on a "Find your recipe" table, each recipe leading with a Focus path. See [Find your field](../optics/focus_dsl.md#find-your-field) and [Find your recipe](../optics/cookbook.md#find-your-recipe).
- **The first lane ends in a capstone and a self-check** ([#1036](https://github.com/higher-kinded-j/higher-kinded-j/pull/1036), [#1039](https://github.com/higher-kinded-j/higher-kinded-j/pull/1039), [#1041](https://github.com/higher-kinded-j/higher-kinded-j/pull/1041)). What a Path Is Made Of, Updates That Can Fail and Many Edits at Once are rewritten. Six pages pause for checkpoints the build proves, a capstone builds an order desk, and two new tutorials practise them. See [Check Your Understanding](../optics/self_check.md).
- **Two lookup pages translate what you already write into optics** ([#1046](https://github.com/higher-kinded-j/higher-kinded-j/pull/1046)). Coming from Lombok, Streams and Switch sets each wither, stream and `switch` habit beside its path, and says when plain Java wins. Coming from Monocle or Haskell maps their names, and the six behaviours that differ. See [Look It Up](../optics/ch7_intro.md).
- **Compositions read as Java, and two hard rules come with a story** ([#1037](https://github.com/higher-kinded-j/higher-kinded-j/pull/1037), [#1040](https://github.com/higher-kinded-j/higher-kinded-j/pull/1040)). The chapter and the `hkj-optics` skill write `Lens.andThen(Prism) = Affine` instead of `>>>`. Composition Rules opens on a short dialogue, and the Lenses page tells how a constructor that lowercases an email quietly breaks a lens. See [Optic Composition Rules](../optics/composition_rules.md).
- **Every diagram draws each optic in one shape** ([#1044](https://github.com/higher-kinded-j/higher-kinded-j/pull/1044), [#1049](https://github.com/higher-kinded-j/higher-kinded-j/pull/1049), [#1055](https://github.com/higher-kinded-j/higher-kinded-j/pull/1055)). A legend on the introduction gives every optic type its own shape, and every diagram fits a phone's screen and describes itself to a screen reader. Each deeper page opens with what you will be able to do ([#1042](https://github.com/higher-kinded-j/higher-kinded-j/pull/1042)). See [How the optic types relate](../optics/ch_intro.md#how-the-optic-types-relate).
- **Production Readiness opens on ten questions and holds the cost facts** ([#1043](https://github.com/higher-kinded-j/higher-kinded-j/pull/1043), [#1050](https://github.com/higher-kinded-j/higher-kinded-j/pull/1050)). The questions run from what a lens update allocates to whether generated classes stay stable across versions. Each optic's allocations and reads are stated in one place, naming the method behind each fact. See [Production Readiness](../optics/production_readiness.md).
- **Every example is compiled, and every name keeps one shape** ([#1020](https://github.com/higher-kinded-j/higher-kinded-j/pull/1020), [#1047](https://github.com/higher-kinded-j/higher-kinded-j/pull/1047), [#1048](https://github.com/higher-kinded-j/higher-kinded-j/pull/1048), [#1101](https://github.com/higher-kinded-j/higher-kinded-j/pull/1101), [#1114](https://github.com/higher-kinded-j/higher-kinded-j/pull/1114), [#1119](https://github.com/higher-kinded-j/higher-kinded-j/pull/1119), [#1120](https://github.com/higher-kinded-j/higher-kinded-j/pull/1120)). A test holds each value the Optics pages state. A type name such as `Order` means one thing across the Optics and Mapping chapters, the skills and the annotations' javadoc. See [Affines](../optics/affine.md).

### Glossary and reference pages {#glossary-and-reference}

- **The Effect Paths glossary gains nine entries** ([#1143](https://github.com/higher-kinded-j/higher-kinded-j/issues/1143), [#1237](https://github.com/higher-kinded-j/higher-kinded-j/issues/1237), [#1245](https://github.com/higher-kinded-j/higher-kinded-j/issues/1245), [#1264](https://github.com/higher-kinded-j/higher-kinded-j/issues/1264)): `CompletableFuturePath`, `EitherOrBothPath`, `EitherPath`, `IdPath`, `IOPath`, `LazyPath`, `MaybePath`, `OptionalPath` and `VTaskPath`. Each gives a definition and its factories on `Path`, and four list their main operations. See [Effect Paths](../glossary/effect-paths.md#completablefuturepath).
- **The glossary gains entries across type classes, optics and concurrency** ([#1037](https://github.com/higher-kinded-j/higher-kinded-j/pull/1037), [#1184](https://github.com/higher-kinded-j/higher-kinded-j/issues/1184), [#1226](https://github.com/higher-kinded-j/higher-kinded-j/issues/1226), [#1227](https://github.com/higher-kinded-j/higher-kinded-j/issues/1227), [#1235](https://github.com/higher-kinded-j/higher-kinded-j/issues/1235)). The type classes gain `Alternative`, `Foldable`, `MonadZero` and `Traverse`, the optics `Getter`, `Ixed` and `Each`, and the core effect types `Reader`, `State` and `Writer`. Concurrency gains `Retry` and `Saga`, and the optics list `andThen`, `modifyF` and path widening. See [Type Classes](../glossary/type-classes.md).
- **Glossary entries name methods their types have** ([#1155](https://github.com/higher-kinded-j/higher-kinded-j/issues/1155), [#1225](https://github.com/higher-kinded-j/higher-kinded-j/issues/1225)): `getOptional` and `build` for a `Prism`, `getAll` and `preview` for a `Fold`, and `asFold().getAll(source)` for a `Traversal`. The check that compiles the book's examples now counts every error, where it skipped any that mentioned "preview". See [Prism](../glossary/optics.md#prism).
- **The cheat sheet and the Type Conversions tables match the code** ([#1142](https://github.com/higher-kinded-j/higher-kinded-j/issues/1142), [#1170](https://github.com/higher-kinded-j/higher-kinded-j/issues/1170), [#1171](https://github.com/higher-kinded-j/higher-kinded-j/issues/1171), [#1221](https://github.com/higher-kinded-j/higher-kinded-j/issues/1221)). The Path Types table gains `FreeApPath` and `VStreamPath`, and names the methods that extract a `LazyPath`, a `GenericPath` and a `WithStatePath`. Two conversion rows give the function `toTryPath` and `toIdPath` take. See [Path Types](../cheatsheet.md#path-types).
- **The Capability Interfaces page matches the interfaces** ([#1182](https://github.com/higher-kinded-j/higher-kinded-j/issues/1182)): `OptionalPath` is only `Chainable` and falls back with `orElsePath`, Combinable lists `zipWith`, and Effectful names `guarantee`. See [Capability Interfaces](../effect/capabilities.md).
- **`WriterT` joins the transformer overview** ([#1195](https://github.com/higher-kinded-j/higher-kinded-j/issues/1195)): the overview, its Path table and Supported Types now list all six transformers. See [WriterT](../transformers/transformers.md#writert-accumulating-output-across-effect-boundaries).
- **The `hkj-test` page lists every assertion class** ([#1193](https://github.com/higher-kinded-j/higher-kinded-j/issues/1193)): `EitherOrBothAssert`, `NonEmptyListAssert`, `FieldErrorAssert` and the Kind-narrowing wrappers join its table, in the book and in the module's README. See [What Is Covered](../tooling/test_assertions.md#what-is-covered).
- **`StandardCodecs.locale()` says a variant keeps its case** ([#1202](https://github.com/higher-kinded-j/higher-kinded-j/issues/1202)): `sl-rozaj` and `sl-ROZAJ` both round-trip, as two different locales, while a case-folded language, region or private-use subtag is still refused. See [Standard codecs](../mapping/codecs.md#standard-codecs).
- **The contributing guide clones your own fork, and lists every module** ([#1194](https://github.com/higher-kinded-j/higher-kinded-j/issues/1194)): its first steps now clone your fork, so the later push reaches it, and the module list includes `hkj-bom` and `hkj-test`. See [Contributing](../CONTRIBUTING.md).

### Other pages {#other-pages}

- **`@PathSource` has a page of its own** ([#999](https://github.com/higher-kinded-j/higher-kinded-j/issues/999)): [Custom Paths with `@PathSource`](../effect/path_source.md) says when a generated Path is worth it over `GenericPath`, what each capability generates, and how `errorType` types recovery.
- **The Resource page gives the order finalisers run in** ([#1019](https://github.com/higher-kinded-j/higher-kinded-j/issues/1019)): the order they were added, where it said the reverse. It also says which exception is reported when a release and a finaliser both throw. See [Finaliser Behaviour](../monads/vtask_resource.md#finalizer-behaviour).
- **`modifyAllEither` says it keeps the first error, not that it stops there** ([#1031](https://github.com/higher-kinded-j/higher-kinded-j/pull/1031)): every focused value is still validated, and the result keeps the first error in traversal order. The javadoc and the examples now say so too. See [`modifyAllEither`: First Error Only](../optics/optics_extensions.md#modifyalleither-first-error-only).
- **`maxNavigatorDepth` says what it does** ([#1030](https://github.com/higher-kinded-j/higher-kinded-j/issues/1030)). A value of `1` makes a navigator's own navigation methods return plain paths, and a larger value, the default included, ends no chain. Its javadoc and Tutorial 19 now say so. See [Controlling navigator generation](../optics/focus_navigation.md#controlling-navigator-generation).
- **The Focus pages say what a wildcard inside a container's element gives you** ([#813](https://github.com/higher-kinded-j/higher-kinded-j/issues/813)). A `List<Box<? extends Widget>>` widens to a `TraversalPath` over `Box<? extends Widget>`, wildcard kept. `BoxFocus.value()` needs a named type argument, so the page shows the lens to write instead. See [A wildcard inside the element](../optics/focus_containers.md#a-wildcard-inside-the-element).
- **Javadoc examples name monoids that exist** ([#1056](https://github.com/higher-kinded-j/higher-kinded-j/pull/1056)): `Fold`, `IndexedFold`, `TraversalPath`, `ConstApplicative` and the Focus package overview show `Monoids.integerAddition()` and its siblings, where they named `Monoids.intSum()` and `Monoid.of`.
- **Every chapter's Previous and Next links follow its contents** ([#1086](https://github.com/higher-kinded-j/higher-kinded-j/pull/1086), [#1093](https://github.com/higher-kinded-j/higher-kinded-j/pull/1093)). Ninety-five pages across eight chapters had stale or missing links, several skipping a page added since. The book check now finds every chapter itself, so a new one is held to its contents too.

---

## Upgrading from 0.4.11 {#upgrading}

Many changes stop the build, most with a message that names the fix. Read [Before you upgrade](#before-you-upgrade) first, then [What a running program can notice](#runtime-changes), which lists the changes the compiler cannot point at.

### Before you upgrade {#before-you-upgrade}

- **Run `MigrateDeprecationsTo0_5_0` before you move to 0.5.0** ([#445](https://github.com/higher-kinded-j/higher-kinded-j/issues/445), [#452](https://github.com/higher-kinded-j/higher-kinded-j/issues/452), [#1260](https://github.com/higher-kinded-j/higher-kinded-j/issues/1260), [#1262](https://github.com/higher-kinded-j/higher-kinded-j/issues/1262)): take `hkj-openrewrite` 0.5.0 for the run, and keep the project on 0.4.11 until it finishes. Its recipes find every removed API only on 0.4.x. Rename each element field its `widenFields` recipe ([#1106](https://github.com/higher-kinded-j/higher-kinded-j/issues/1106)) marks before you move, so the compiler finds the calls through it. See [0.5.0 deprecation migration](../tooling/openrewrite.md#050-deprecation-migration).
- **Under `-Werror`, run `MigrateDeprecationsTo0_6_0` in the same pass** ([#1105](https://github.com/higher-kinded-j/higher-kinded-j/issues/1105)): on 0.5.0 a call to a deprecated Focus path shortcut fails a `-Werror` build. Every typed form the recipe writes is in 0.4.11, so it can run beside `MigrateDeprecationsTo0_5_0`. Replace by hand each call it marks.
- **Replace `Void` with `Unit` where a success has nothing to return** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065), [#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `Either<E, Void>` and `Try<Void>` can no longer hold their null. A `VTask<Void>`, `IOPath<Void>` or other lazy computation fails when its step returns null. `VTask.exec`, `Path.vtaskExec` and `Path.ioRunnable` give `Unit`. The `MigrateDeprecationsTo0_5_0` run marks each site.
- **Recompile what was compiled against a generated Focus class** ([#1033](https://github.com/higher-kinded-j/higher-kinded-j/issues/1033), [#1214](https://github.com/higher-kinded-j/higher-kinded-j/issues/1214)): a field or a hop that gains a navigator returns a navigator class. A module compiled against the earlier class fails with `NoSuchMethodError` until it is rebuilt.
- **Recompile what calls `chunk`, `chunkWhile`, `mapChunked` or `SagaBuilder.start()`** ([#580](https://github.com/higher-kinded-j/higher-kinded-j/issues/580)): `start()` returns a new type, so a module compiled against 0.4.11 fails with `NoSuchMethodError`. A chunk keeps its erased type, so such a module links, then fails with `ClassCastException` where it reads a chunk as a `List`.

### What a running program can notice {#runtime-changes}

#### Mapping {#runtime-mapping}

- **A date codec over a formatter that cannot parse what it writes throws when built** ([#1144](https://github.com/higher-kinded-j/higher-kinded-j/issues/1144)): `StandardCodecs.localDate(formatter)` and `offsetDateTime(formatter)` throw `IllegalArgumentException`, with the formatter's `DateTimeException` as its cause. Such a codec used to be built, then refuse every input.
- **A bean property is read through its accessor, never a word declared before it** ([#1128](https://github.com/higher-kinded-j/higher-kinded-j/issues/1128)): a `getaway()` or `island()` declared before `getAway()` or `isLand()` read `away` or `land`, and the accessor now does.
- **A merge fill can parse through a generic spec in this compilation** ([#1123](https://github.com/higher-kinded-j/higher-kinded-j/issues/1123)): a dependency's spec for exactly the pair used to fill it. It now resolves as a mapping's nest does, your own specs first. A leaf named after the component, delegating to the dependency's Impl, keeps the old fill.

#### Optics {#runtime-optics}

- **A `@GenerateFocus` record field, or a container of one, returns a navigator** ([#1033](https://github.com/higher-kinded-j/higher-kinded-j/issues/1033), [#1103](https://github.com/higher-kinded-j/higher-kinded-j/issues/1103), [#1214](https://github.com/higher-kinded-j/higher-kinded-j/issues/1214)): a `getClass()` check sees the navigator class, as it does the form a hop from a wider navigator returns. A navigator equals only a navigator of the same class over an equal path, never the plain path it wraps.
- **A navigator is a path** ([#1033](https://github.com/higher-kinded-j/higher-kinded-j/issues/1033)): one from a record that wrote `generateNavigators = true` passes `instanceof FocusPath`, and an overload taking a `FocusPath` is chosen over one taking `Object`.
- **A 0.4.x navigation call can compile against a path method instead** ([#1033](https://github.com/higher-kinded-j/higher-kinded-j/issues/1033), [#1106](https://github.com/higher-kinded-j/higher-kinded-j/issues/1106), [#1243](https://github.com/higher-kinded-j/higher-kinded-j/issues/1243)): take a record that writes `generateNavigators = true`, with a `Map` field `byTicker` of records with a `last` field. `byTicker().last()` then calls the path's own `last()`, which throws `ClassCastException`. A note at the field and `MigrateDeprecationsTo0_5_0` ask for `last` to be renamed before you move. See [Compiler Messages](../optics/compiler_errors.md#generatefocus-no-navigator-over-elements).
- **A null `@Nullable` container has no focus** ([#1062](https://github.com/higher-kinded-j/higher-kinded-j/issues/1062)): a generated Focus path that steps into one reads nothing, and a modification leaves it null, where both threw `NullPointerException`.
- **A read through an `Optional` or a `Maybe` reads a null focus as absent** ([#767](https://github.com/higher-kinded-j/higher-kinded-j/issues/767)): `Fold.preview`, `TraversalPath.preview` and `toMaybePath`, and `FocusPath.toMaybePath` used to throw `NullPointerException`. So did `FocusPath.asAffine`, `headOption`, `IxedInstances.get` and the list optics `listAt`, `listHead` and `listLast`, whose setters, and the list prisms' `build`, now write a null.
- **Other optics carry a null focus through** ([#767](https://github.com/higher-kinded-j/higher-kinded-j/issues/767)): `find` passes over a null focus to the next match, where it threw. `Setter.forList`, and `FocusPaths.listElements`, `arrayElements`, `listCons`, `listSnoc`, `listTail`, `listInit` and `mapValues`, carry a null element. `IxedInstances.contains` counts an index holding a null as present, and `Plans.preflight` walks a batched traversal to the end, so `truncated` is now `false`.
- **A traversal's Effect Path bridges leave a null focus out** ([#1017](https://github.com/higher-kinded-j/higher-kinded-j/issues/1017)): `toStreamPath`, `toVStreamPath` and `VStreamPath.fromEach` used to stream it, and `toListPath` and `toNonDetPath` threw `NullPointerException`. `TraversalPath.fold` counts it as the monoid's empty value, where `Monoids.string()` appended `"null"`. `getAll` still keeps every focus.
- **A raw `modifyF` under `Either` or `Try` cannot pass over a null element** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): `filtered` keeps an element it passes over by lifting it with `of`, as do a prism and an affine that miss. Under those applicatives `of` now refuses null. `TraversalExtensions.modifyAllEither` and its siblings keep the element.
- **`OpticPrograms.get` fails on a null focus, naming `preview`** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): a Free program always gives a value, so the direct and logging interpreters throw `NullPointerException`, and `ValidationOpticInterpreter` records an error. `StateOps.view` on a null focus fails the same way.
- **A map an optic writes iterates in the source's order** ([#1022](https://github.com/higher-kinded-j/higher-kinded-j/pull/1022)): it came back in hash order, so printed output or a test of a map's iteration order can change. `FocusPaths.mapValues` over an empty map returns a new map.
- **A `TreeMap` keeps its order but not its type** ([#1022](https://github.com/higher-kinded-j/higher-kinded-j/pull/1022)): `forMapValuesCollecting(TreeMap::new)` keeps both when the keys sort in their natural order. A `TreeMap` with its own comparator needs a collector that builds the new `TreeMap` with that comparator.
- **The function a predicate does not pick is no longer called** ([#1026](https://github.com/higher-kinded-j/higher-kinded-j/pull/1026)): by `modifyWhen` and `branch` on a `Traversal`, and by `modifyWhen` and `modifyBranch` on a `Lens`. `Lens.setIf` no longer calls `set` on a failed predicate. `Traversals.speculativeTraverseList` is unchanged: it applies both functions to every element.
- **A query's predicate is not called after its answer** ([#1056](https://github.com/higher-kinded-j/higher-kinded-j/pull/1056)): `find`, `exists` and `all`, on `Fold`, `IndexedFold` and `TraversalPath`, stop at the focus that settles them, so a logging or counting predicate sees fewer calls. A `Fold` of your own whose `foldMap` catches every exception still gives the same answer, but walks on to its end.
- **A read no longer runs a traversal's rebuild** ([#1056](https://github.com/higher-kinded-j/higher-kinded-j/pull/1056)): a `Traversal` of your own whose rebuild logs or counts no longer runs it for `Traversals.getAll`, a `TraversalPath`'s reads, `IndexedTraversals.toIndexedList`, `getAll` and `length`, or `asIndexedFold()`.
- **`OptionalTraverse.traverse` rejects a function that returns null** ([#1056](https://github.com/higher-kinded-j/higher-kinded-j/pull/1056)). For a present value, a `null` from the function used to give an empty `Optional`. It now throws `NullPointerException`, as the other `Traverse` instances do.
- **Sequenced lists are unmodifiable at every size** ([#1018](https://github.com/higher-kinded-j/higher-kinded-j/issues/1018)): `IndexedTraversals.sequenceList`, `ForTraversal.toList` and `ForIndexed.toIndexedList` now return one, as `Traversals.traverseList` does. So do `IndexedTraversals.forList` on a non-empty list and `Traversals.traverseArray` on an empty array. Copy the result into a new `ArrayList` to change it.
- **`ForTraversal.toList` and `ForIndexed.toIndexedList` honour the filters** ([#1018](https://github.com/higher-kinded-j/higher-kinded-j/issues/1018)): they collect only the elements that pass `filter` and `filterIndex`, as `modify` and `set` already did. They used to return every focused element. See [Collecting Results](../functional/for_optics.md#collecting-results).
- **The list optics that returned an `ArrayList` now return an unmodifiable list** ([#1063](https://github.com/higher-kinded-j/higher-kinded-j/issues/1063)): `.each()`, `.at(i)`, `.head()` and `.last()` on a Focus path, `ListTraversals`, `AtInstances.listAt` and `listAtWithPadding`, and `IxedInstances.listIx`. Changing one throws `UnsupportedOperationException`, so copy it first. With nothing at the index, `.at(i)` and `listIx` return your own list. See [What each collection optic builds](../optics/production_readiness.md#collection-optics).
- **A Focus path over a field declared as `ArrayList` fails when `.each()`, `.at(i)`, `.head()` or `.last()` writes** ([#1063](https://github.com/higher-kinded-j/higher-kinded-j/issues/1063)): storing the rebuilt list, an unmodifiable `List`, back throws `ClassCastException`. `.each()` already failed on a one-element list. Declare the field as `List`, or use `each(EachInstances.fromIterableCollecting(ArrayList::new))`.
- **A `@ViaConstructor` lens with no `parameterOrder` sets through its constructor** ([#906](https://github.com/higher-kinded-j/higher-kinded-j/issues/906)): the order is read from the constructor's parameter names, where `set` used to throw `UnsupportedOperationException`.
- **A spec lens whose focus is wider than its getter's wrapper passes the value as it is** ([#906](https://github.com/higher-kinded-j/higher-kinded-j/issues/906)): with `Number` over an `int` getter, it used to be cast to `int` for `withX(int)`. Where the wither, setter or constructor is also overloaded for a wider type, such as `withX(Object)`, that overload is now the one called.
- **A `@ViaCopyAndSet` lens over a `withX` setter hands back what it returns** ([#1108](https://github.com/higher-kinded-j/higher-kinded-j/issues/1108), [#1128](https://github.com/higher-kinded-j/higher-kinded-j/issues/1128)): not the copy, where every method of that name the new value can be passed to returns the source type. A fluent `withX` behaves as before, and so does every `set` method. A `witheMail`, as JavaBeans spells one for `eMail`, is a `withX` setter too.
- **A `@ViaBuilder` lens over a Lombok `@Singular` collection, or through a protobuf-java message's `addAllTags` or `putAllPoints`, replaces the collection** ([#1110](https://github.com/higher-kinded-j/higher-kinded-j/issues/1110)): `set` used to add the new elements to the ones `toBuilder()` copied. Code that set a one-element list to add an element now keeps only that element; append with `modify` instead.
- **An `Each` of your own that declares `eachWithIndex()` without `@Override` is no longer indexed** ([#1261](https://github.com/higher-kinded-j/higher-kinded-j/issues/1261)): it still compiles, as a method of its own, but `supportsIndexed()` now returns false for it. Implement `EachIndexed` instead.

#### Effect Paths: null values {#runtime-effect-paths}

- **A null success throws, or fails a `Try`** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): `Either.right(null)`, `Try.success(null)` and the Path factories throw `NullPointerException`, and so does an `Either` or `EitherPath` `map` whose function returns null. In `Try.of`, `map`, `ap`, `recover` and `TryPath`, a null result gives a `Failure` holding one. A `VResultPath` fails when it runs.
- **A null error throws** ([#1077](https://github.com/higher-kinded-j/higher-kinded-j/issues/1077)): `Either.left(null)`, `EitherT.left`, `raiseError(null)` on `EitherMonad` and `EitherTMonad`, and a `mapLeft` or `bimap` whose error function returns null throw `NullPointerException`, where they built `Left(null)`.
- **`raiseError(null)` throws on `Maybe`, `Optional`, `MaybeT` and `OptionalT`** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): it gave `Nothing` or an empty `Optional`. Pass `Unit.INSTANCE`. Every `MonadError` now refuses a null error.
- **`recover(ma, null)` throws on `EitherMonad`, `TryMonad` and `EitherTMonad`** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): whether or not `ma` failed. A null fallback used to give `Right(null)` or `Success(null)`. `Try.orElseGet` refuses a supplier that returns null, as `Validated.orElseGet` does; `orElse(null)` still hands a null back.
- **The conversions into an `Either` check their error even when it is not needed** ([#1077](https://github.com/higher-kinded-j/higher-kinded-j/issues/1077)): `Maybe.toEither` checks its error, or its supplier, even on a `Just`. The optics helpers `getEither`, `getValidated`, `modifyEither` and `modifyValidated` check theirs even when the focus is there. `Try.toEither` throws `NullPointerException` for a mapper that returns null, where it threw `KindUnwrapException`.
- **The one-argument `toEitherPath` and `toTryPath` on a `FocusPath` throw on a null focus** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): they gave `Right(null)` and `Success(null)`. The message names the form that takes an error. `EitherPath.focus(FocusPath)` throws on a null focus too, naming `focus(path.nullable(), …)`, and `TryPath.focus` gives a `Failure`.
- **`ErrorContext.io` treats a null result as a failure** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): its error mapper sees a `NullPointerException`, where the context held `Right(null)`.
- **A function that returns null fails the computation** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065), [#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): a callable, supplier, `map` function, combiner or recovery function that returns null fails the lazy computation when it runs, and the `NullPointerException` names that function. So `VTask.of(() -> { work(); return null; })` fails, where `run()` handed the null back.
- **A null result fails where a computation becomes a `Try` or an `Either`** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): `runSafe`, `toTryPath`, `asTry`, `Scope.joinSafe` and `EffectBoundary.runSafe` give a `Failure` holding a `NullPointerException`. `joinEither` and `FreePath.attempt` give a `Left`, and `catching` hands it to its mapper.
- **A null value passed to a lazy factory throws** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `VTask.succeed`, `State.pure`, `Reader.constant`, `Writer.value`, `Lazy.now`, `Trampoline.done`, `Free.pure`, `FreeAp.pure`, `VStream.of`, `Context.succeed`, the Path factories such as `Path.ioPure` and `Path.id`, and `of` on their monads throw `NullPointerException`. So do the constructors of `StateTuple`, `Writer`, `Trampoline.Done`, `Free.Pure` and `VStream`'s `Emit` and `Seed`.
- **`StateT`, `ReaderT` and `WriterT` refuse a null value** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `of` on their monads, `WriterT.of` and `WriterT.writer` throw on one. A function given to `ReaderT.reader`, or to `map` or `ap` on their monads, that returns null fails the computation. `ReaderTMonad.of(null)` over `Optional` gave an empty `Optional`.
- **`VTask.run()` and `runAsync()` refuse a null from a hand-written task** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): a `VTask` written as a lambda that returns null throws `NullPointerException` from `run()`, and fails the future from `runAsync()`. `PathOps.firstVTaskSuccess` counts such a null as a failure and tries the next path. `VTaskContext.runOrElse` refuses a null default, and `runOrElseGet` a handler that returns null.
- **An `IOPath`, `Par` and a `Scope` refuse a null from a hand-written `IO` or task** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `IOPath.unsafeRun()` throws `NullPointerException` for one, as does each step that runs it, and `EffectBoundary.embed`. `Par`'s combinators and a `Scope` that needs every subtask fail on such a null, and an accumulating `Scope` records it as an error.
- **A stream refuses a null element** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `VStream.of` throws on one, and `fromList`, `fromIterator`, `fromStream` and `generate` fail the stream when they pull one. `fromPublisher` cancels its subscription and fails the stream. `StreamPath` does the same for its sources. `PathOps.traverseVStream` still hands a null item in its list to the function.
- **A `CompletableFuturePath` fails on a future that completes with null** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078), [#1072](https://github.com/higher-kinded-j/higher-kinded-j/issues/1072)): `Path.future` and `fromFuture` check the value as it arrives: map a `runAsync` future to `Unit` first. Cancelling either future cancels both. `withTimeout` and `completeOnTimeout` complete only the new path's future, and the latter refuses a null default. `CompletableFutureMonad` keeps the JDK's null.
- **An `IdPath` always holds a value** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `Path.id(null)` throws, as does `Path.idPath` given an `Id` holding null. `FocusPath.toIdPath` throws on a null focus, naming `toMaybePath`, and so does `IdPath.focus`, naming `focus(path.nullable())`. `Id` and `IdMonad` still hold a null, since every optic's `modify` runs through them.
- **The lazy Paths' `focus` refuses a null focus** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): `IOPath`, `VTaskPath` and `VStreamPath` fail when they run, and the message names the `path.nullable()` form to use. They used to hold the null.
- **`Context.ask` fails on a key bound to null** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): read a key that may hold null with `asks`. `SecurityContext.requireAuthenticated` still fails an anonymous user with `UnauthenticatedException`.
- **A null in a saga fails its step** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065), [#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): a step whose task returns null fails like any other step, and so does a `map` that returns null. The steps before it are compensated, under `Saga.run` and `Saga.runSafe` alike. A null result gave `Right(null)` from `runSafe`, and `run` handed it back.
- **A null result from a deferred trampoline step names its supplier** ([#1153](https://github.com/higher-kinded-j/higher-kinded-j/issues/1153), [#1161](https://github.com/higher-kinded-j/higher-kinded-j/issues/1161)). It is still a `NullPointerException`, raised when the step runs: from `run()` for `Trampoline.defer`, and from the deferred step for `TrampolinePath.defer` and `Path.trampolineDefer`. No call site needs to change.

#### Effect Paths: resources, cancellation and streams {#runtime-resources}

- **A finaliser that used to be skipped now runs** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024)): when a `VStream` stops early, a terminal operation fails, or a function given to an operator such as `map`, `filter` or `mapTask` throws. A finaliser that throws then fails the operation that stopped, such as `take` or `headOption`. Closing runs the innermost finaliser first, as completing does.
- **A `bracket` whose `use` function throws releases the resource** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024)): before the failure reaches the caller. It used to leave the resource acquired.
- **Closing a pulled `bracket` stream releases its latest run** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024)): closing the head of a `bracket` after a pull releases the resource that pull acquired, where it used to release nothing.
- **`onFinalize` runs its finaliser for each consumption of a `VStream`** ([#1018](https://github.com/higher-kinded-j/higher-kinded-j/issues/1018)): a second consumption, or one after `close()`, used to skip it. It now also runs when the source fails as it is pulled. `close()` runs it for the latest consumption only, unless that consumption has run it already. See [onFinalize: Lightweight Cleanup](../monads/vstream_resources.md#onfinalize-lightweight-cleanup).
- **`VStream.defer` calls its supplier when the pulled `VTask` runs** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024)): not when `pull()` is called, and again on each run. `generate`, `iterate` and `unfold` call their functions then too. `distinct`, `chunk` and `chunkWhile` now give the same step each time a pulled `VTask` runs, and a `distinct` tail pulled again replays its elements.
- **`VStreamPar.parEvalMap`, `parEvalMapUnordered` and the `VStream` applicative's `ap` read their input when the pulled `VTask` runs** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024)): not when `pull()` is called, and afresh on each run.
- **`VStreamPar.merge` starts a producer for each run of its pulled `VTask`** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024), [#1073](https://github.com/higher-kinded-j/higher-kinded-j/issues/1073)): not when `pull()` is called. Closing it stops that run's producer and waits for each unfinished source to close, even with an interrupt pending. `close()` now stops the producer when its `VTask` runs, not when it is called. See [merge: Concurrent Multi-Source Consumption](../monads/vstream_parallel.md#merge-concurrent-multi-source-consumption).
- **Cancelling a `toPublisher` subscription runs the stream's finalisers** ([#1024](https://github.com/higher-kinded-j/higher-kinded-j/issues/1024)): once the subscriber has requested elements. It used to leave the stream open.
- **`VStreamThrottle.throttle` gives each consumption its own window** ([#1018](https://github.com/higher-kinded-j/higher-kinded-j/issues/1018)): each consumption's first emission opens its first window. A later or concurrent consumption used to share the first one's window.
- **`Resource.onFailure` runs its action when the use fails** ([#1019](https://github.com/higher-kinded-j/higher-kinded-j/issues/1019)): it used to do nothing. The action runs before the release, both when the use fails and when a step composed after it fails while the resource is held, such as `map`'s function throwing. See [onFailure Callback](../monads/vtask_resource.md#onfailure-callback).
- **`Resource.map` releases the resource when its function throws** ([#1019](https://github.com/higher-kinded-j/higher-kinded-j/issues/1019)): it used to leave the resource acquired.
- **A `Resource` rejects a null value** ([#1019](https://github.com/higher-kinded-j/higher-kinded-j/issues/1019)): an `acquire` given to `make` or `fromAutoCloseable` that returns null, a null passed to `pure`, and a function given to `map` that returns null now fail with `NullPointerException`. Hold an absent value as a `Maybe` instead. See [Factory Methods](../monads/vtask_resource.md#factory-methods).
- **A failing use keeps its own exception when cleanup fails too** ([#1019](https://github.com/higher-kinded-j/higher-kinded-j/issues/1019), [#1053](https://github.com/higher-kinded-j/higher-kinded-j/issues/1053)): in a `Resource`, and in `IOPath.bracket`, `bracketIO`, `guarantee`, `guaranteeIO` and `VTaskPath.guarantee`, the cleanup's exception is kept among the use's suppressed exceptions. When a release and a finaliser both throw, the release's exception is reported. See [Exception Safety](../monads/vtask_resource.md#exception-safety).
- **`and(second, third)` releases its first resource when two releases throw one exception** ([#1019](https://github.com/higher-kinded-j/higher-kinded-j/issues/1019)): the same exception instance from both used to fail with `IllegalArgumentException` and leave the first resource acquired.
- **An exception from `close()` fails `withResource`, `withResourceIO` and `fromAutoCloseable`** ([#1053](https://github.com/higher-kinded-j/higher-kinded-j/issues/1053)): it used to be dropped. After a successful use, `IOPath` throws it unchecked, a `SQLException` as the cause of a `RuntimeException`, and the task from `Resource.use` fails. After a failed use, it is suppressed onto the use's. To ignore it, pass `bracket` or `Resource.make` a release that catches it.
- **`withResource` and `withResourceIO` reject a null resource** ([#1053](https://github.com/higher-kinded-j/higher-kinded-j/issues/1053)): a resource supplier that returns null now fails with `NullPointerException` before the use runs. The use used to receive the null. Check for an absent resource before building the path.
- **Cleanup runs with the interrupt status cleared** ([#1019](https://github.com/higher-kinded-j/higher-kinded-j/issues/1019), [#1053](https://github.com/higher-kinded-j/higher-kinded-j/issues/1053), [#1057](https://github.com/higher-kinded-j/higher-kinded-j/issues/1057), [#1073](https://github.com/higher-kinded-j/higher-kinded-j/issues/1073)): after any use, in `Resource`, `IOPath`, `VTaskPath`, `VStream` finalisers, `bracketOutcome` and each `Saga` compensation. So a blocking close is not cut short, and a `Scope` that timed out during the use waits for it. The status is restored afterwards, and `withResource` sets it when `close()` throws an `InterruptedException`.
- **A cancelled `bracketOutcome` use leaves the thread interrupted** ([#1057](https://github.com/higher-kinded-j/higher-kinded-j/issues/1057)): when the use throws an `InterruptedException`, the interrupt status is set again once the release has run. The thread's next blocking call then throws `InterruptedException` at once. To tell a cancellation from other failures, map `InterruptedException` to its own error in `onDefect`.
- **A task's checked exception passes through `Retry`, `Bulkhead` and `protectWithFallback` unwrapped** ([#1072](https://github.com/higher-kinded-j/higher-kinded-j/issues/1072)): `Bulkhead.protect`, `Retry.retryTask` and its fallback and recovery forms, and `withBulkhead` and `withRetry` on `VTaskPath` and `VResultPath`, wrapped it in `VTaskExecutionException`. `CircuitBreaker.protectWithFallback` wrapped it, and an `Error`, in `RuntimeException`. A `retryOn` predicate, a fallback and `RetryExhaustedException.getCause()` now see the exception itself.
- **An interrupt stops a retry** ([#1072](https://github.com/higher-kinded-j/higher-kinded-j/issues/1072)): an attempt that throws `InterruptedException` is not tried again, whatever the predicate, and the exception passes through as thrown, past any fallback. A thread left interrupted between attempts stops the retry with its status set and `RetryExhaustedException`, which a fallback still handles. A recovery task does not start after either.
- **A timeout passes a cancellation on** ([#1072](https://github.com/higher-kinded-j/higher-kinded-j/issues/1072)): when a thread waiting on `VTask.timeout` is interrupted, so is the task, and `InterruptedException` follows once it stops or the time runs out. It used to leave the task running and report its result or a `TimeoutException`. `withTimeout` on `VTaskPath`, `IOPath`, `VResultPath` and `EitherPath` builds on it.
- **A cancelled call trips no circuit breaker and skips a `ResilienceBuilder` fallback** ([#1072](https://github.com/higher-kinded-j/higher-kinded-j/issues/1072)): a call that throws `InterruptedException` counts as neither a success nor a failure, whatever `recordFailure` says. The fallback lets it pass.
- **`VTask.run()`, `runSafe()` and `Try.attempt` keep a cancellation's interrupt status** ([#1072](https://github.com/higher-kinded-j/higher-kinded-j/issues/1072)): each sets it again for an `InterruptedException` it wraps or returns as the failure, and the Path forms built on them follow. So a retry around `IOPath.withTimeout` sees the cancellation and stops, and the thread's next blocking call throws at once. The status used to be cleared.
- **An extremely long duration no longer throws** ([#1072](https://github.com/higher-kinded-j/higher-kinded-j/issues/1072)): a duration too long to count in nanoseconds, such as `ChronoUnit.FOREVER.getDuration()`, counts as about 292 years in timeouts, waits, retry delays, throttle windows and `retryAfter`. Some of those threw `ArithmeticException`. Sub-millisecond parts are kept, where `RetryPolicy` and several waits dropped them.
- **`Scope.firstComplete` and `ScopeJoiner.firstComplete()` cancel the other subtasks once one completes** ([#1052](https://github.com/higher-kinded-j/higher-kinded-j/issues/1052)): they used to wait for every subtask. Those still running are interrupted and those not yet started never start, so work a slower subtask does afterwards, such as writing a cache, no longer happens. Move work that must finish out of the race.
- **`ScopeJoiner.joiner()` returns a new `Joiner` on each call** ([#1052](https://github.com/higher-kinded-j/higher-kinded-j/issues/1052)): code that opened a scope with one call and read `result()` from another now reads a `Joiner` no scope has used. That gives an empty result or an exception, so use the value the scope's `join()` returns.
- **A `Scope`'s timeout cancels its subtasks** ([#1052](https://github.com/higher-kinded-j/higher-kinded-j/issues/1052)): when it expires, those still running are interrupted, and the run fails with a `TimeoutException` once they have stopped. So a subtask that ignores interrupts now delays the failure. Its subtasks now see the caller's `ScopedValue` bindings, which they used to find unbound.
- **An interrupt while a timed `Scope` waits fails the run** ([#1052](https://github.com/higher-kinded-j/higher-kinded-j/issues/1052)): with an `InterruptedException`, as it does for an untimed `Scope`. A timed one used to ignore it and keep waiting.
- **`Par` and `PathOps` copy the list they are given when called** ([#1052](https://github.com/higher-kinded-j/higher-kinded-j/issues/1052)): `Par.race`, `all` and `traverse`, and `PathOps.sequenceVTask`, `traverseVTask`, `firstVTaskSuccess`, `parSequenceIO` and `raceIO`. Changing the list afterwards no longer changes what a run does. A null task or path in a list, or in `Scope.forkAll`'s, is rejected then, not when it runs.
- **`Scope.named` names the scope in a JSON thread dump** ([#1052](https://github.com/higher-kinded-j/higher-kinded-j/issues/1052)): the name used to be dropped. `named(null)` now throws `NullPointerException`, where it was accepted.
- **A chunk is not a `List`** ([#580](https://github.com/higher-kinded-j/higher-kinded-j/issues/580)): what `chunk`, `chunkWhile` and `mapChunked` hand over prints as `NonEmptyList[1, 2]` and never equals a `List`, even one holding the same elements. Compare `chunk.toJavaList()` where code checked a chunk against a `List`.
- **A large chunk size or concurrency on a short stream returns its elements** ([#1238](https://github.com/higher-kinded-j/higher-kinded-j/issues/1238), [#1249](https://github.com/higher-kinded-j/higher-kinded-j/issues/1249)): `chunk(Integer.MAX_VALUE)` and `parEvalMap` with that concurrency used to throw `OutOfMemoryError` before reading an element. The other chunked and parallel calls, and their `VStreamPath` and `VStreamContext` forms, now return their elements too. A size that already returned chunks returns the same ones.

#### Effect Paths: lists and `StateT` {#runtime-lists-and-statet}

- **`StateT` equality leaves out the `Monad`** ([#445](https://github.com/higher-kinded-j/higher-kinded-j/issues/445)): two `StateT` values wrapping the same function instance are equal, with equal hash codes, whichever `Monad` built them. `toString` prints only `runStateTFn`, so a test comparing the old text fails.
- **A list from `PathOps` or `Par` cannot be changed** ([#1017](https://github.com/higher-kinded-j/higher-kinded-j/issues/1017)): the `sequence` and `traverse` methods, `Par.all` and `Par.traverse` handed back their working `ArrayList`. So changing the list inside an `EitherPath` changed what every later `run()` saw. Changing one now throws `UnsupportedOperationException`, so copy it into a new `ArrayList` first.
- **`TrampolineUtils.traverseListStackSafe` and `sequenceStackSafe` return an unmodifiable list** ([#1018](https://github.com/higher-kinded-j/higher-kinded-j/issues/1018)): as `Traversals.traverseList` does. Changing it throws `UnsupportedOperationException`, so copy it into a new `ArrayList` first.

#### Spring {#runtime-spring}

- **The declarative HTTP client refuses an empty body for `EitherPath` and `VTaskPath`** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): it gave `Right(null)`. It now throws an `EmptyResponseBodyException`, or fails the task, after the request has been made. A retry policy that retries every throwable would send it again, so exclude that type. Declare `Unit` where the endpoint sends no body.
- **A `Unit` success writes no body** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065)): a handler that writes one value wrote `Unit` as the JSON string `"INSTANCE"`. It now sends the resolved status with no body, as a `void` controller method does.
- **A lazy Path whose step returns null writes the failure status** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): an `IOPath`, `VTaskPath`, `CompletableFuturePath` or `FreePath` handler wrote `null` as the body. The step now fails, as a null does in any lazy computation.
- **A JSON `Either` holding `"right": null` or `"left": null` is refused** ([#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065), [#1077](https://github.com/higher-kinded-j/higher-kinded-j/issues/1077)): `EitherDeserializer` reports it as bad input, as `ValidatedDeserializer` does for a null value.
- **An SSE frame holding `null` fails the stream** ([#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078)): a stream from `HkjClientExchange.vstream` fails with `SseStreamException`, as for any other frame it cannot read.

#### Testing {#runtime-testing}

- **The lens and affine law harnesses check set-set in both orders** ([#1169](https://github.com/higher-kinded-j/higher-kinded-j/issues/1169), [#1175](https://github.com/higher-kinded-j/higher-kinded-j/issues/1175)). A fixture that broke set-set only in the untested order now fails. A lawful lens or affine still passes with the values either way round.
- **Lens and affine law harnesses accept one value equal to the current focus** ([#812](https://github.com/higher-kinded-j/higher-kinded-j/issues/812)). Existing lawful fixtures still pass, and two equal supplied values are still rejected. A setter that ignores updates still fails set-get on the other value.
- **`assertAffineLaws` no longer requires `set` to leave an absent target alone** ([#1023](https://github.com/higher-kinded-j/higher-kinded-j/pull/1023)): a test that relied on it to catch an affine writing there now passes. Add `AffineLaws.assertSetNoOpWhenAbsent(affine, absentSource, value)` to keep that check.

#### Build and tooling {#runtime-build}

- **Uppercase warning settings take effect on Turkish systems** ([#1168](https://github.com/higher-kinded-j/higher-kinded-j/issues/1168)): remove any workaround that lowercased `WARNING` before passing it to the checker. Generated for-comprehension lambda names now use `i` rather than dotless `ı`; tuple constant names are unchanged. Numeric log counts use the root locale's digits. No source changes are required.

### What stops a build that compiled {#build-changes}

| Area | Now fails the build | Do this | Issue |
|---|---|---|---|
| Mapping | A `@GenerateMerge` fill whose pair two generic specs in this compilation both cover, where neither is more specific, which a dependency's spec for exactly the pair used to fill | Add the leaf the error spells, delegating to the spec you mean, or declare a spec for exactly the pair in this compilation | [#1123](https://github.com/higher-kinded-j/higher-kinded-j/issues/1123) |
| Mapping | A bean with a method that only starts like an accessor, such as `getx()` and `setx(int)` for `x`, where nothing else reads or writes the component in its place: `getaway()` beside `setAway(String)` alone, or `getid()` read through a `@ReadOnly` marker | Rename it as the error offers, such as `getX()` or `getId()`. Where it is not meant to carry the component, name its property in an `@Unmapped` marker, where the error offers one | [#1128](https://github.com/higher-kinded-j/higher-kinded-j/issues/1128) |
| Mapping | A PATCH bean's setter with no getter spelt `seteMail(String)`, as JavaBeans spells one for `eMail`, or `set_links(String)` | Remove the setter, or rename it after the component it should update and give it a getter, or name its property in an `@Unmapped` marker | [#1128](https://github.com/higher-kinded-j/higher-kinded-j/issues/1128) |
| Optics | Under a nullness checker, `FocusPaths.nullable()` assigned to an `Affine<X, X>` | Declare `Affine<@Nullable X, X>`, the type `Affines.nullable()` returns | [#767](https://github.com/higher-kinded-j/higher-kinded-j/issues/767) |
| Optics | Under a nullness checker, `AtInstances.listAtWithPadding(null)` for a non-null element type | Pad with a value, or name a nullable element type: `AtInstances.<@Nullable String>listAtWithPadding(null)` | [#767](https://github.com/higher-kinded-j/higher-kinded-j/issues/767) |
| Optics | `nullable()` whose result is bound to another focus type, as in `AffinePath<S, Integer> x = stringPath.nullable()`, which compiled as an unchecked cast | Convert the focus with a `Getter` or an `Iso` composed after `nullable()` | [#1017](https://github.com/higher-kinded-j/higher-kinded-j/issues/1017) |
| Optics | Code that holds or chains the Focus path of a field reaching a container whose `TraversableGenerator` names no optic expression, as the `AffinePath` or `TraversalPath` it widened to: its declared type, a call such as `getAll` or `via`, or a navigator method through it | Use the path the Focus method now returns, which stops at the container, and widen it with `.some(affine)` or `.each(each)`; or override `generateOpticExpression()` in the generator | [#1059](https://github.com/higher-kinded-j/higher-kinded-j/issues/1059) |
| Optics | A prism onto an inner class of a generic class, used at its raw type: in a type argument, as in `Prism<Outlines.Outline, Outlines.Dot> p = OutlinePrisms.dot()`, or read back as a particular `Outlines<String>.Dot` | Write `Outlines<?>.Dot` or use `var`; where the value must be an `Outlines<String>.Dot`, cast to it under `@SuppressWarnings("unchecked")`, which states the unchecked step | [#886](https://github.com/higher-kinded-j/higher-kinded-j/issues/886) |
| Optics | An `@InstanceOf` target that is an inner class of a generic class, where the source does not pin an enclosing argument that the focus names, such as `Outer<String>.Plain` from a `Node<String>`, or that bounds a pinned argument, as `Z extends X` does, either of which compiled as an unchecked narrowing | Declare the focus with a wildcard there, `Outer<?>.Plain`, or narrow with `@MatchWhen` | [#886](https://github.com/higher-kinded-j/higher-kinded-j/issues/886) |
| Optics | `var x = iso.andThen(traversal);` (or `traversal.andThen(affine)` or `traversal.andThen(iso)`) followed by assigning another kind of optic to `x`; your own `use(Optic)` and `use(Traversal)` overloads now pick the `Traversal` one | Declare `x` as the `Optic` you mean to hold, or call the overload you mean | [#1021](https://github.com/higher-kinded-j/higher-kinded-j/pull/1021) |
| Optics | A `@ViaConstructor` `parameterOrder` that passes a value where the constructor's parameter names say another belongs, such as `{"y", "x"}` for `Point(int x, int y)`, including a lens named after an accessor no parameter is named after | Write the order the error offers, which follows those names, or rebuild with the strategy it names | [#906](https://github.com/higher-kinded-j/higher-kinded-j/issues/906) |
| Optics | A `@ViaConstructor` lens with no `parameterOrder`, whose `set` threw, where the constructors' parameter names give no order the call rebuilds through, a longer constructor might hold more, the lens's focus fits no constructor, or the source type is abstract | Write the order, declare the focus the error names, or rebuild with the strategy it names | [#906](https://github.com/higher-kinded-j/higher-kinded-j/issues/906) |
| Optics | A spec lens whose focus is wider than its getter's wrapper, such as `Number` over an `int` getter, rebuilding through an overloaded wither, setter or constructor none of whose overloads takes the focus; setting a value of another kind used to throw `ClassCastException` | Declare the focus as the wrapper, `Integer` | [#906](https://github.com/higher-kinded-j/higher-kinded-j/issues/906) |
| Optics | Under `-Werror`, a written `@ViaConstructor` `parameterOrder` that passes nothing for a value a longer constructor, or a record component, takes, such as `{"amount"}` beside `Fee(long amount, String currency)` | Write the order the warning offers, or annotate the lens method `@SuppressWarnings("shorter-constructor")` where the shorter constructor works the value out | [#1067](https://github.com/higher-kinded-j/higher-kinded-j/issues/1067) |
| Optics | A `@ViaConstructor` `parameterOrder` that passes a value where a variable-arity constructor's parameter names say another belongs, such as `{"note", "label"}` for `Sticker(String label, String note, String... parts)` | Write the order the error offers | [#1067](https://github.com/higher-kinded-j/higher-kinded-j/issues/1067) |
| Optics | Under `-Werror`, a `@ViaCopyAndSet` setter spelt as a wither whose result the lens cannot keep, such as `Shrub withTitle(String)` on a `Hedge extends Shrub`, or a fluent `withX` returning a raw or base type | Annotate the lens method `@SuppressWarnings("dropped-result")` where the method sets the copy it is called on; otherwise follow the warning's fix | [#1108](https://github.com/higher-kinded-j/higher-kinded-j/issues/1108) |
| Optics | A spec lens whose generated code made an unchecked conversion or call, which compiled, with javac's note on unchecked operations, unless `-Xlint:unchecked` and `-Werror` were both on: an accessor that reads a raw type for a focus with type arguments, a focus written raw for a method or constructor that takes one with them, a raw accessor passed to a `@ViaConstructor` parameter that has them, a method called through a raw builder or a raw supertype, or a focus written raw for a method that takes the type a wildcard of the source or its builder stands for | Read and rebuild through members that keep their type arguments, or declare the lens over the raw type with `@SuppressWarnings("rawtypes")` on the lens method, or declare the spec with a type argument in place of the wildcard, or point `toBuilder` at a method that hands back the builder with its type arguments, or rebuild with another strategy | [#905](https://github.com/higher-kinded-j/higher-kinded-j/issues/905), [#1110](https://github.com/higher-kinded-j/higher-kinded-j/issues/1110) |
| Optics | With `widenCollections = true`, a Focus method over an array of a reference type, or over a container holding one, such as `String[] tags` or `List<String[]> rows`, used as its old path or chained with `.each(EachInstances.arrayEach())`. A `String @Nullable []` was an `AffinePath` to the array | Drop the `.each(EachInstances.arrayEach())` and use the path over the elements, or keep the array's own path from its lens, `FocusPath.of(SLenses.tags())`, adding `@GenerateLenses` if the record lacks it | [#1062](https://github.com/higher-kinded-j/higher-kinded-j/issues/1062) |
| Optics | With `widenCollections = true`, an array whose element is an SPI container, such as an `Either` or a `Map`, with a wildcard argument or none, such as `Either<?, Widget>[]` or `Either[]` | Name the element's type arguments, as in `Either<String, Widget>[]`, or drop `@GenerateFocus` and add or keep `@GenerateLenses`, as for an `Either<?, Widget>` component | [#1062](https://github.com/higher-kinded-j/higher-kinded-j/issues/1062) |
| Optics | A record that writes `generateNavigators = true` and holds another `@GenerateFocus` record, or a `List`, `Optional` or other container of one, that has a field named like a no-argument path method, such as `last`, `head`, `each`, `segments`, `toLens` or `toPath` | Rename that field, or keep the field that navigates into it a plain path, as the error says | [#1033](https://github.com/higher-kinded-j/higher-kinded-j/issues/1033), [#1103](https://github.com/higher-kinded-j/higher-kinded-j/issues/1103) |
| Optics | A `@GenerateFocus` `includeFields`, `excludeFields` or `widenFields` name that matches no component of the record, which changed nothing, or, as the only name in `includeFields`, left every field without a navigator | Write the component's name, as the error offers, or take the name out | [#1104](https://github.com/higher-kinded-j/higher-kinded-j/issues/1104) |
| Optics | Under `-Werror`, a `@GenerateFocus` field list that changes nothing: `excludeFields` beside a non-empty `includeFields`, `includeFields` or `excludeFields` with `generateNavigators = false`, `widenFields` beside `widenCollections = true`, or a `widenFields` name whose field is widened anyway or holds no container | Remove the list or the name, or the setting that leaves it unread, as the warning says | [#1104](https://github.com/higher-kinded-j/higher-kinded-j/issues/1104) |
| Optics | Under `-Werror`, an `includeFields` name matching a component that has no navigator, such as a `String`, an unannotated record or an unwidened `Map` of annotated records, including wildcard targets and wildcard containers | Follow the [warning's fix line](../optics/compiler_errors.md#generatefocus-includefields-no-navigator): name a navigator field, annotate the target record, widen the container, or remove `includeFields` and turn navigators off | [#1181](https://github.com/higher-kinded-j/higher-kinded-j/issues/1181), [#1201](https://github.com/higher-kinded-j/higher-kinded-j/issues/1201) |
| Optics | With `generateNavigators = true` and no `widenCollections`, a `Map` or a third-party collection of `@GenerateFocus` records, such as `ImmutableList<Position> positions`, used as the navigator over its elements: `.positions().ticker()`, `getAll`, `modifyAll`, `count`, or assigned to a `TraversalPath` | Run `MigrateDeprecationsTo0_5_0` ([Before you upgrade](#before-you-upgrade)), or add the field to `widenFields`, as the note at the field says, or set `widenCollections = true` | [#1033](https://github.com/higher-kinded-j/higher-kinded-j/issues/1033), [#1106](https://github.com/higher-kinded-j/higher-kinded-j/issues/1106) |
| Optics | A Focus method's result for a field of another `@GenerateFocus` record, or of a `List`, `Optional` or other container of one, with its type inferred: a `var` then assigned another path, such as `lines = lines.filter(p)`, or a generic call such as `Stream.of(OrderFocus.customer()).toList()` assigned to a `List<FocusPath<Order, Customer>>`. A navigator's hop into one record from a wider path, such as `orders().customer()`, is inferred the same way | Name the path type: declare the variable, or give the call its type argument | [#1033](https://github.com/higher-kinded-j/higher-kinded-j/issues/1033), [#1103](https://github.com/higher-kinded-j/higher-kinded-j/issues/1103), [#1214](https://github.com/higher-kinded-j/higher-kinded-j/issues/1214) |
| Optics | On a record that writes `generateNavigators = true`, two components that both reach `@GenerateFocus` records and whose names differ only in the case of their first letter, such as `items` and `Items`. A `List`, `Optional` or other container of one now gets a navigator too. Both navigator classes would be `ItemsNavigator`, so the processor refuses the later one. On the default, the later one keeps its plain path, with a note | Rename one of the components, or keep the later one a plain path, as the error's fix line says | [#1103](https://github.com/higher-kinded-j/higher-kinded-j/issues/1103), [#1259](https://github.com/higher-kinded-j/higher-kinded-j/issues/1259) |
| Optics | Code calling a lens `@ImportOptics` generated through a word, such as `draw()` through `withdraw(long)` beside `getDraw()`; a class whose only wither was such a word is now refused | Drop the call: `withdraw` never set `draw`. Where the class has a method that does set the field, name it with `@Wither` in a [spec interface](../optics/optics_spec_interfaces.md) | [#1128](https://github.com/higher-kinded-j/higher-kinded-j/issues/1128) |
| Optics | A call to a Focus path's `each()`, `some()`, `at(index)`, `atKey(key)`, `traverseOver(traverse)`, `head()`, `last()`, `tail()`, `init()`, `cons()`, `snoc()`, `headTail()` or `initLast()` (a `[removal]` warning) | Run `MigrateDeprecationsTo0_6_0`, or use the typed form that [Deprecated for removal in 0.6.0](#deprecated) names | [#1105](https://github.com/higher-kinded-j/higher-kinded-j/issues/1105) |
| Optics | A call to `Each.eachWithIndex()`, deprecated since 0.4.7, or an `@Override` of it | Declare the instance as `EachIndexed`, the type `EachInstances.listEach()` returns, and call `indexedTraversal()`, which returns the traversal itself rather than an `Optional`. Where you hold an `Each` you cannot redeclare, match it with `instanceof EachIndexed<?, S, A> indexed`. For an `Each` of your own with an index, implement `EachIndexed` | [#1261](https://github.com/higher-kinded-j/higher-kinded-j/issues/1261) |
| Effect Paths | A `Monad` passed to `new StateT<>(fn, monad)`, `StateT.create`, `StateTKindHelper.stateT` or `StateT.mapT` | Run the recipe as [Before you upgrade](#before-you-upgrade) says, or drop the argument by hand | [#445](https://github.com/higher-kinded-j/higher-kinded-j/issues/445) |
| Effect Paths | A two-component `StateT` record pattern, or `StateT::create` or `StateT::new` as a `BiFunction` | Match the one component, or take a `Function`, by hand | [#445](https://github.com/higher-kinded-j/higher-kinded-j/issues/445) |
| Effect Paths | A `StateT` in `var` or a chained call over a witness with a type argument, such as `EitherKind.Witness<E>`, which now infers `Object` for it | Name the types, as in `StateT.<S, EitherKind.Witness<E>, A>create(fn)`, or declare the variable | [#445](https://github.com/higher-kinded-j/higher-kinded-j/issues/445) |
| Effect Paths | `evalStateT` and `execStateT` without a `Monad`, deprecated since 0.4.6 | Pass the outer `Monad<F>`, the one `Instances.stateT` took, as the last argument | [#445](https://github.com/higher-kinded-j/higher-kinded-j/issues/445) |
| Effect Paths | `StateT.monadF()`, deprecated since 0.4.6 | Keep a reference to the outer `Monad<F>` you built the stack with, and use it in place of the call | [#445](https://github.com/higher-kinded-j/higher-kinded-j/issues/445) |
| Effect Paths | `Try.fold` or `TryPath.fold`, deprecated since 0.4.6 | Run the recipe as [Before you upgrade](#before-you-upgrade) says, or call `foldFailureFirst` with the two functions swapped | [#452](https://github.com/higher-kinded-j/higher-kinded-j/issues/452) |
| Effect Paths | `StateTKind.narrowK`, deprecated since 0.4.5, or `KindValidator.narrowWithPattern`, deprecated since 0.4.4 | Run the recipe as [Before you upgrade](#before-you-upgrade) says, or rename the call to `narrow` or `narrowHolder`. Declare an argument typed `Kind<?, A>` as the `Kind<StateTKind.Witness<S, F>, A>` that `narrow` takes | [#1260](https://github.com/higher-kinded-j/higher-kinded-j/issues/1260) |
| Effect Paths | `@PathConfig` on a package, deprecated since 0.4.11, which now fails with `cannot find symbol` | Run the recipe as [Before you upgrade](#before-you-upgrade) says, or delete it and its import, since it changed nothing generated | [#1262](https://github.com/higher-kinded-j/higher-kinded-j/issues/1262) |
| Effect Paths | `EFFECTFUL` or `ACCUMULATING`, deprecated since 0.4.11, wherever it is named, as in `@PathSource` or a `switch` over `PathSource.Capability` | Run the recipe as [Before you upgrade](#before-you-upgrade) says, or write `CHAINABLE` for `EFFECTFUL` and `RECOVERABLE` for `ACCUMULATING`, which generate the same class. In a `switch` that already lists the replacement, delete the `case` | [#1262](https://github.com/higher-kinded-j/higher-kinded-j/issues/1262) |
| Effect Paths | A call to `ScopeJoiner.resultEither()` (a `[removal]` warning) | Use `joinEither()` on the `Scope`, as [Deprecated for removal in 0.6.0](#deprecated) says | [#1052](https://github.com/higher-kinded-j/higher-kinded-j/issues/1052) |
| Effect Paths | Under a nullness checker, a nullable value passed to `Either.right`, `Try.success`, `Path.right`, `Path.success`, `TryKindHelper.success`, `EitherT.right`, the `Either.Right` or `Try.Success` constructor, or `of` on `EitherMonad`, `TryMonad` or `EitherTMonad` | Pass `Unit.INSTANCE` for a step with nothing to return, or route a value that may be missing through `Maybe` | [#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065) |
| Effect Paths | Under a nullness checker, a nullable error passed to `Either.left`, `EitherT.left`, the `Either.Left` constructor, or `raiseError` on `EitherMonad` or `EitherTMonad` | Pass an error that says what went wrong, or `Unit.INSTANCE` where the error type is `Unit` | [#1077](https://github.com/higher-kinded-j/higher-kinded-j/issues/1077) |
| Effect Paths | A `bracketOutcome` release whose outcome parameter is declared `Either<E, B>`, or that reads the error as an `E` | Take `Either<Maybe<E>, B>`, and read the error through the `Maybe` | [#1077](https://github.com/higher-kinded-j/higher-kinded-j/issues/1077) |
| Effect Paths | Under a nullness checker, a nullable value passed to a lazy value factory such as `VTask.succeed`, `State.pure`, `Lazy.now` or `Path.ioPure`, or to `of` on their monads or on a transformer's, or a hand-written `VTask`, `IO` or `Reader` lambda that returns one | Pass `Unit.INSTANCE` for a step with nothing to return, or route a value that may be missing through `Maybe` | [#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078) |
| Effect Paths | Under a nullness checker, a nullable error passed to `raiseError` on any `MonadError`, such as `MaybeMonad` or `OptionalMonad` | Pass an error that says what went wrong, or `Unit.INSTANCE` where the error type is `Unit` | [#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078) |
| Effect Paths | A `PathProvider` that overrides `monadError()`, or code that reads `monadError()` or `PathRegistry.unregister` as a value that may be null | Return `Optional.of(...)` from the override, and read the `Optional` | [#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078) |
| Effect Paths | Under a nullness checker reading `hkj-core` from the classpath, a nullable argument to a type it treated as unannotated: `VStream`, `Const`, `WriterT`, the Path SPI or the validation helpers | Pass a value, or route one that may be missing through `Maybe` | [#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078) |
| Effect Paths | Code that treats a chunk from `chunk`, `chunkWhile` or `mapChunked` as a `List`: a type such as `VStream<List<A>>` or `List<List<A>>`, a reference to a method taking `List<A>`, such as `VStream::fromList`, a `List` method such as `get`, `getLast` or `isEmpty`, or a stored `Function<List<A>, List<B>>` passed to `mapChunked` | Declare `NonEmptyList<A>`, and use `head()` and `last()` for `get(0)` and `getLast()`. Pass `chunk.toJavaList()` where a `List` is needed, add `.map(NonEmptyList::toJavaList)` to keep a `VStream<List<A>>`, or pass a stored function as `fn.compose(NonEmptyList::toJavaList)` | [#580](https://github.com/higher-kinded-j/higher-kinded-j/issues/580) |
| Effect Paths | A variable, `var` included, or a parameter that holds `SagaBuilder.start()`, such as one a loop adds steps to | Declare it a `SagaSteps`, the type both stages share, and switch on it to build, as [Steps From Data](../resilience/saga.md#steps-from-data) shows | [#580](https://github.com/higher-kinded-j/higher-kinded-j/issues/580) |
| Effect Paths | `build()` on `SagaBuilder.start()` with no step added, which threw `IllegalStateException` | Add a step before `build()` | [#580](https://github.com/higher-kinded-j/higher-kinded-j/issues/580) |
| Spring | An `@HkjHttpClient` method whose success type is `Void` | Replace `Void` with `org.higherkindedj.hkt.Unit` | [#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065) |
| Testing | `EitherAssert.hasRightNull()` | Delete the assertion: a `Right` can no longer hold null | [#1065](https://github.com/higher-kinded-j/higher-kinded-j/issues/1065) |
| Testing | `EitherAssert.hasLeftNull()` | Delete the assertion: a `Left` can no longer hold null | [#1077](https://github.com/higher-kinded-j/higher-kinded-j/issues/1077) |
| Testing | `IOAssert.hasValueNull()`, `hasNullValue()` on `VTaskAssert`, `LazyAssert`, `StateAssert` and `WriterAssert`, and `ReaderAssert`'s `producesNull()` | Delete the assertion: a lazy computation can no longer give null | [#1078](https://github.com/higher-kinded-j/higher-kinded-j/issues/1078) |
| Testing | Under a nullness checker, `getException()` or `getValue()` on `IOAssert` or `VTaskAssert` assigned to, or returned as, a non-null type | Declare the result `@Nullable` and check it, or assert on the outcome instead, as `hasValue` and `throwsException` do | [#1094](https://github.com/higher-kinded-j/higher-kinded-j/issues/1094) |

### Deprecated for removal in 0.6.0 {#deprecated}

The `MigrateDeprecationsTo0_6_0` recipe runs each recipe the table names. See [0.6.0 deprecation migration](../tooling/openrewrite.md#060-deprecation-migration).

| Deprecated | Replacement | Recipe |
|---|---|---|
| A Focus path's `each()`, on `FocusPath`, `AffinePath`, `TraversalPath` and a navigator | `each(EachInstances.listEach())` | `ReplaceUntypedPathShortcutsRecipe` |
| A Focus path's `some()` | `some(FocusPaths.optionalSome())` | `ReplaceUntypedPathShortcutsRecipe` |
| A Focus path's `at(index)` | `via(FocusPaths.listAt(index))` | `ReplaceUntypedPathShortcutsRecipe` |
| A Focus path's `atKey(key)` | `via(FocusPaths.mapAt(key))` | `ReplaceUntypedPathShortcutsRecipe` |
| A Focus path's `traverseOver(traverse)` | `each(EachInstances.fromTraverse(traverse))` | `ReplaceUntypedPathShortcutsRecipe` |
| A Focus path's `head()`, `last()`, `tail()` and `init()` | `via(FocusPaths.listHead())`, `via(FocusPaths.listLast())`, `via(FocusPaths.listTail())` and `via(FocusPaths.listInit())` | `ReplaceUntypedPathShortcutsRecipe` |
| A Focus path's `cons()` and `headTail()` | `via(FocusPaths.listCons())` and `via(FocusPaths.listHeadTail())` | `ReplaceUntypedPathShortcutsRecipe` |
| A Focus path's `snoc()` and `initLast()` | `via(FocusPaths.listSnoc())` and `via(FocusPaths.listInitLast())` | `ReplaceUntypedPathShortcutsRecipe` |
| `ScopeJoiner.resultEither()` | `joinEither()` on the `Scope`, in place of `join()`; for a `StructuredTaskScope` you open yourself, the value its `join()` returns, or the cause of the `FailedException` it throws | By hand |

---

**Previous:** [Unreleased: 0.5.1](unreleased.md)
**Next:** [v0.4.11](v0_4_11.md)
