Common Compiler Errors

Find the message your compiler printed, then read what it means and how to fix it.

This page is for the moment a build fails and you want to know what the message means. Find the fragment your compiler printed in Find your message and follow it to its entry. Every entry says what the message means, gives the fix, and keeps the reasoning in a Why you can open if you want it.

Error, warning, or note?

  • An error stops the build. Almost everything here is an error.
  • A warning stops the build only under -Werror. A processor warning cannot be suppressed, so the remedy is the fix rather than an annotation.
  • A note stops nothing. It shows in the compiler output as Note: ..., and tells you that something you asked for was quietly not applied.

Rows below and headings on the page say which, wherever it is not an error.


Find your message

From @GenerateLenses, @GenerateFocus, @GenerateTraversals and friends (entries):

The message saysWhat it means
cannot find symbol: class XLensesThe processor has not run, or the IDE has not indexed the generated sources
can only be applied to records@GenerateLenses or a sibling is on a class
can only be applied to sealed interfaces or enums@GeneratePrisms is on something else
does not bindA subtype needs a parameter its clause leaves free
names a type variableThe @GenerateIsos method's Iso type is not fully concrete
'x' is not staticThe @GenerateIsos method is an instance method
'x' takes parametersThe @GenerateIsos method takes arguments
does not return an Iso with both type argumentsThe @GenerateIsos method returns something else
cannot be reached from 'p'The @GenerateIsos method is not visible from the generated package
The generated companion is a top-level classA type the generated companion names is private, or hidden in another package
has a wildcard type argument, has a raw EitherA widened SPI container, such as Either, is raw or has a wildcard
cannot navigate toA record that writes generateNavigators = true reaches a record with a field named like a no-argument path method
has no navigator intoNote. The same clash, on a record that leaves navigators to the default: the field keeps its plain path
has no navigator over itsNote. A record that writes generateNavigators = true leaves a container of navigable records unwidened
has no AffineForm, has no TraversalFormNote. A record that writes generateNavigators = true reaches a record with a field named like a wider path's own method, so a hop from that path returns the plain path
which is not a component ofincludeFields, excludeFields or widenFields names a field the record does not have
whose field cannot have a navigatorWarning. includeFields names a component that cannot return a navigator
changes nothing, becauseWarning. A field list the record's other settings never read
is widened without it, holds no container widenFields widensWarning. A widenFields name whose path widening cannot change
no traversal was generated for componentNote. @GenerateTraversals found no generator for a container
Multiple TraversableGenerator SPI providers with equal priorityWarning. Two generators claim one type and neither outranks the other
the annotation on record component 'X.y' is not appliedNote. @TraverseField is on something that is not a Kind with a declared witness
names a witness the processor does not recogniseNote. A Kind field's witness has no registered Traverse, so nothing widens it

From @ImportOptics and spec interfaces (entries):

The message saysWhat it means
lists no classes to importWarning. The annotation names no class, so nothing is generated
which has no optics to importThe class list holds a primitive, an array or void
and also lists classes to importA spec interface carries a class list it does not read
extends OpticsSpec only throughA spec reaches OpticsSpec<S> through another interface
implements OpticsSpec but is not an interfaceA class is written as a spec
inherits the optic methodA spec's optic method is declared on another interface
carries no copy strategy annotationA spec Lens method names none of the four copy strategies
is a default methodA spec interface method has a body
which is a type variableOpticsSpec<S> names a type parameter rather than a type
which names the raw type 'Box'The source type is missing its type arguments
which hides the 'T' of its enclosing classAn imported inner class reuses a type-parameter name of its enclosing class
rather than as the List interface@ThroughField's lens focuses a concrete container, or another interface
which the spec does not declare@ThroughField has no lens for the field to compose with
which the spec declares static or private@ThroughField names a lens the generated class does not carry
hands back as 'String'@ThroughField's declared focus is not what the traversal returns
is not a subtype of source type@InstanceOf names a class outside the hierarchy
which no '...' can beNo value of the source can be the class
so the bound has nothing to nameA pinned parameter is bounded by one the source leaves free
outside its parameter's boundAn argument the source pins breaks its bound
which the test cannot narrow toThe focus promises a type argument instanceof cannot check
narrows to '...', which is not a '...'The @InstanceOf class is not assignable to the declared focus
does not resolve to a typecopyConstructor is not a fully qualified class name
which 'S' does not extend or implementcopyConstructor names a type that is not a supertype
is not public and so cannot be named fromcopyConstructor names a type the generated class cannot see
and no constructor acceptsNo copy constructor takes the supertype you named
is written with a wildcard type argumentA constructor rebuild cannot be written for a wildcard source type
is an abstract class, is an interfaceA constructor rebuild of an abstract class or interface
not the source typeThe method a @Wither call binds returns something other than the source type
for the generated lens to call@Wither names a method the source type does not have, or one the generated class cannot reach
takes the lens's focus typeNo method of the name @Wither gives takes the lens's focus
cannot choose betweenMore than one overload takes the focus, none more closely than the rest
is static, so the generated lens cannot rebuildThe overload the focus binds is a static method
for the generated lens to read the value it focusesA strategy's getter, or the lens method that stands in for it, names no accessor
reads '...', not the lens's focusThe accessor reads a value the lens cannot hand back as its focus
only through an unchecked conversion, so the generated call to ... is uncheckedA read or rebuild through a raw type is unchecked
inherits 'toBuilder()' fromA subclass inherits a toBuilder() whose builder builds its parent
for the generated lens to set throughA setter names no method of the type it is called on
hands back '...', which is not a builderA @ViaBuilder step leads somewhere the next call cannot be made
hands back a type bounded byNone of the types a @ViaBuilder step is bounded by has the next method
stands in takes no value at allA setter's parameter is the type a wildcard of the source, or of the builder, stands for
is static, so the generated lens cannot set throughThe setter the focus binds is a static method
takes one argumentNo method the setter names takes an argument, and the focus is a wildcard
names no argument for the lens's ownA @ViaConstructor order leaves out the lens method
'parameterOrder' passesA @ViaConstructor order contradicts the constructor's parameter names
No constructor of '...' takesNo constructor takes a @ViaConstructor order's arguments
The generated call 'new ...' cannot choose betweenSeveral constructors take those arguments equally well
No 'parameterOrder' is writtenParameter names give no one order to read
parameter named after the lens's ownNo constructor takes a parameter for the lens
not a constructor it was read fromAn order read from names binds another constructor
leaves out what '...' takesAn order read from names misses a longer constructor
passes nothing forWarning. A written @ViaConstructor order rebuilds through a shorter constructor
drops what '...' returnsWarning. A withX setter's new value may be lost
pairs more than one wither with the fieldNote. Two withers reach one field name, and one lens is generated
focuses '...', which is not a '...'A generated prism's focus is a value rather than a variant of the source
cannot find symbol, inside XPrisms.javaA @MatchWhen predicate or getter name is misspelt
requires a prism hint annotationA spec Prism method has neither @InstanceOf nor @MatchWhen

From @GeneratePathBridge and @PathVia (entries):

The message saysWhat it means
which no Path wrapsThe method returns a type outside the bridged set
names the raw type 'Y'The signature is missing type arguments somewhere
is the wildcard '?'A bridged Validated names its error type as a wildcard
has the same name as 'Y'sA method type parameter hides one of the interface's
the bridge cannot call 'x'The method is static or private
is already takenTwo @PathVia methods produce the same bridge signature
is not a method name@PathVia(name = ...) is not a Java identifier
cannot be reached from 'p'Under targetPackage, part of the signature is not visible there
no @PathVia method was foundWarning. The interface has nothing to bridge

From javac, on code you wrote (Focus DSL chains, Free Monad):

The message saysWhat it means
List<Object> cannot be converted to, after traverseOvertraverseOver's element type is not pinned; each(EachInstances.fromTraverse(...)) pins it
Incompatible types, after .each().via()Usually one .each() too many
Cannot infer type argument(s)Only the final untyped each() in a chain can infer its element type
no suitable method found for each(...), or for some or viaThe path does not focus on the container the typed step names
each() in org.higherkindedj.optics.focus.FocusPath has been deprecated and marked for removalWarning. A no-argument shortcut, due to go in 0.6.0, has a typed form
::new rejected as a BiFunctionA single-component record has no two-argument constructor
Sealed or non-sealed local classes are not allowedA sealed interface is declared inside a method body
Cannot resolve method 'flatMap(...)'Two Free witness types are being mixed
Free<F, A> cannot be converted to AThe program was never handed to an interpreter

Where the message came from

Three different things can reject your code, and knowing which one spoke narrows the search:

flowchart TD
    accTitle: Where a compiler message came from
    accDescr: Your declaration goes to the processor first, and a refusal there is reported at your declaration, which is most of this page. An accepted declaration becomes a generated file; javac refusing that file reports cannot find symbol inside a class you did not write. If javac refuses your call site instead, the message is a Focus DSL chain or Free Monad error; otherwise the build succeeds.
    D["Your declaration:<br/>@Generate*, or<br/>a spec interface"]
    P{"Does the processor<br/>accept it?"}
    R["Refused at your<br/>declaration.<br/>Most of this page"]
    G["Generated file written"]
    J{"Does javac accept<br/>the generated file?"}
    JG["cannot find symbol,<br/>inside a class<br/>you did not write"]
    C{"Does javac accept<br/>your call site?"}
    CC["Focus DSL chain and<br/>Free Monad errors"]
    OK["Builds"]

    D --> P
    P -->|no| R
    P -->|yes| G
    G --> J
    J -->|no| JG
    J -->|yes| C
    C -->|yes| OK
    C -->|no| CC

    classDef step fill:#8caaee,stroke:#1e66f5,color:#232634
    classDef decision fill:#e5c890,stroke:#df8e1d,color:#232634
    classDef error fill:#e78284,stroke:#d20f39,color:#232634
    classDef ok fill:#a6d189,stroke:#40a02b,color:#232634
    class D,G step
    class P,J,C decision
    class R,JG,CC error
    class OK ok

Most of this page is the first branch. The processor reads your declaration, finds a shape it cannot write code for, and says so where you wrote it. Those messages name the element they rejected, so reading the processor's own output first is quicker than working backwards from a cannot find symbol further down the build.

A cannot find symbol: class XLenses sits outside the diagram altogether: it means the processor never ran.


@GenerateLenses / @GenerateFocus / @GenerateTraversals

"cannot find symbol: class XLenses"

The annotation processor has not run yet, or the IDE has not picked up the generated sources directory. With @ImportOptics, the class is also missing when a type it imports never appeared: the import waits for a type another processor writes, and javac reports that type as missing too. An error reported earlier, or a warning under -Werror, stops annotation processing before a waiting import is read, so fix those first.

Fix. Run a build (./gradlew build or mvn compile). After the build completes, refresh the project in your IDE so it indexes build/generated/sources/annotationProcessor/java/main (Gradle) or target/generated-sources/annotations (Maven).

"@GenerateLenses: can only be applied to records, but 'Foo' is a class"

@GenerateLenses, @GenerateFocus, @GenerateFolds, @GenerateGetters, @GenerateSetters and @GenerateTraversals only apply to records.

Fix. Convert the class to a record. If the type is third-party and you cannot change it, use @ImportOptics on a package-info.java or a spec interface instead.

Why

The annotations target TYPE, so javac itself is happy; the message comes from the processor. The wording varies: @GenerateLenses and @GenerateFocus name the offending type, while the others emit the shorter "The @GenerateTraversals annotation can only be applied to records."

A declaration that produces it

@GenerateLenses
class Voucher {}

"The @GeneratePrisms annotation can only be applied to sealed interfaces or enums."

@GeneratePrisms requires a sealed interface or an enum. A sealed abstract class is rejected too, despite being sealed, because the processor tests the element kind rather than the modifier.

Fix. Make the type a sealed interface and declare its permits clause, or convert it to an enum.

A plain interface fails silently instead

A non-sealed interface passes the processor's guard and produces an empty XPrisms class with no diagnostic at all. If your prisms class exists but has no methods, an unsealed interface is why.

A declaration that produces it

@GeneratePrisms
abstract class Payment {}

"@GeneratePrisms: 'Pair' declares [B], which 'Shape' does not bind"

A permitted subtype needs a type parameter that its implements clause leaves free. @ImportOptics reports the same for an imported sealed interface. The message takes one of two forms:

The message saysThe shape
'Pair' declares [B], which 'Shape' does not bindPair<A, B> implements Shape<A>: the subtype's own B appears nowhere in its clause
The prism for 'Hexagon' declares Y extends X, and 'Shape' does not bind [X]Hexagon<Y extends X> implements Shape<Y>, an inner class of Shapes<X>: a bound names the enclosing class's X, which the clause leaves free

Fix. Bind the free parameter in the clause, as the message shows, giving the sealed interface a type parameter for it if it has none to spare. Where you cannot change the clause, as for a sealed interface from a library, write the prism by hand with Prism.of.

Why

A prism narrows by instanceof, which tests an erasure. A parameter the clause binds is pinned by the hierarchy: under Tagged<T> implements Shape<T>, a Shape<T> that is a Tagged can only be a Tagged<T>. A parameter it leaves free is pinned by nothing, so two callers could read one value at different types, and the second would get a ClassCastException from a call site that compiled cleanly.

An enclosing class's parameter the clause leaves free is written as a wildcard, as Generating the Prisms shows. A bound that names it then has nothing to name: the prism has no X to write in <Y extends X>, and javac refuses Shapes<?>.Hexagon<Y> for such a Y.

Declarations that produce it

@GeneratePrisms
sealed interface Shape<T> permits Pair {}

record Pair<A, B>(A a, B b) implements Shape<A> {}
class Shapes<X> {

    @GeneratePrisms
    sealed interface Shape<T> permits Shapes.Hexagon {}

    final class Hexagon<Y extends X> implements Shape<Y> {}
}

"@GenerateIsos: the iso returned by 'x' names a type variable"

One of the returned Iso's two type arguments is, or contains, a type variable. Both <T> Iso<Box<T>, T> boxIso() and an instance method of a Holder<X> returning Iso<Box<X>, X> do this.

Fix. Give the iso concrete type arguments where the method is declared (Iso<Box<String>, String>), or drop @GenerateIsos and call the method directly.

Why

What gets generated is a public static final field, and a field has nowhere to declare one, so it would name a variable nothing brings into scope.

Note this is about what the iso names, not what the method declares: <T> Iso<Box, String> boxIso() is fine, because T is inferred at the call and never reaches the field's type.

A declaration that produces it

record Box<T>(T value) {}

final class BoxIsos {

    @GenerateIsos
    static <T> Iso<Box<T>, T> box() {
        return Iso.of(Box::value, Box::new);
    }
}

"@GenerateIsos: 'x' is not static"

The annotated method is an instance method. The generated field initialises itself with a static call, and there is no instance to make it on.

Fix. Make the method static.

A declaration that produces it

record Quantity(int units) {}

final class QuantityIsos {

    @GenerateIsos
    Iso<Quantity, Integer> quantity() {
        return Iso.of(Quantity::units, Quantity::new);
    }
}

"@GenerateIsos: 'x' takes parameters"

The annotated method takes arguments. The generated field initialises itself by calling the method with none, and there is nothing for it to pass.

Fix. Take the arguments away, or drop @GenerateIsos and call the method directly.

A declaration that produces it

record Quantity(int units) {}

final class QuantityIsos {

    @GenerateIsos
    static Iso<Quantity, Integer> quantity(int scale) {
        return Iso.of(Quantity::units, Quantity::new);
    }
}

"@GenerateIsos: 'x' does not return an Iso with both type arguments"

The method returns a void, a primitive, an array, a raw Iso, or something that is not an Iso at all. The generated field is typed from the two arguments of the returned Iso, and none of those carries them.

Fix. Return Iso<S, A> naming both, as Iso<Point, Tuple2<Integer, Integer>>.

A declaration that produces it

final class PointIsos {

    @GenerateIsos
    static String point() {
        return "not an Iso";
    }
}

"@GenerateIsos: 'x' cannot be reached from 'p'"

The generated class lives in package p and calls the method from there, but the method, or a type enclosing it, is private, protected or package-private somewhere else. Most often seen with targetPackage.

Fix. Make the method and its enclosing types public, or generate into the package they are already visible from.

A declaration that produces it

final class LengthIsos {

    @GenerateIsos(targetPackage = "com.example.optics")
    static Iso<String, Integer> length() {
        return Iso.of(String::length, "x"::repeat);
    }
}

"@GenerateLenses: record component 'x' of 'X' names 'Y', which cannot be reached from 'p'"

A type the generated companion names is hidden from the package the companion is written into: it, or a class enclosing it, is private, or it comes from another package and is not public. @GenerateLenses and the other optics generators, @ImportOptics, @GenerateAssembly, @GenerateErrorEnvelope, @EffectAlgebra and @PathSource refuse such a type at the annotation. The message names where the companion meets the type: the annotated type itself (record 'Shop.Item' cannot be reached from 'p'), or a component, subtype, bound, field, optic, variant, operation, witness or error type.

Fix. Remove private from the class the message names. From another package, make it public; under targetPackage, removing targetPackage also works for a type beside the annotated one, and the message offers it where it does.

@GenerateLenses: record component 'sku' of 'Item' names 'Sku', which cannot be reached from
'com.example'. The generated companion is a top-level class in that package, where it names
every type it reads or writes, so each has to be visible from there. Remove 'private' from 'Sku'.

Why

The companion, ItemLenses here, is a top-level class beside Shop, not nested inside it, so it cannot see a type Shop keeps private. It names every type it reads or writes, some of them only in a lambda it leaves javac to infer, and javac checks those too. A traversal names only the components it traverses, so a private type on any other component compiles.

A declaration that produces it

class Shop {
    private record Sku(String value) {}

    @GenerateLenses
    record Item(Sku sku) {}
}

"@GenerateFocus: record component 'X.y' has a wildcard type argument in Either<String, ? extends T>"

A container that the processor widens through the optic its SPI generator supplies is declared raw, or with a wildcard type argument. The same error covers Either, Try, Validated, Map and every other such container, and is also reported as "has a raw Either". A Map or another collection draws it only once something widens it. The five built-in containers, Optional, Maybe, List, Set and Collection, never draw it.

Fix. Name the type arguments, Either<String, Widget> rather than Either<String, ? extends Widget>. For a wildcard, you can instead drop @GenerateFocus from the record and keep @GenerateLenses and @GenerateTraversals, which compose no optic instance. The lens keeps the component's type as written. The traversal focuses the type the wildcard stands for: Widget for ? extends Widget, and Object for ? or ? super Widget. A raw container has no element type for @GenerateTraversals to focus, so it draws a note and no traversal there; keep @GenerateLenses alone for one. See Custom Containers.

Why

That optic, such as Affines.eitherRight(), has its own type arguments worked out from the component's type. A raw container gives javac nothing to work from, and a wildcard stands for no one type. A built-in container is exempt because the processor names its instance for the element the arguments stand for, and casts it to the type the field declares.

A declaration that produces it

@GenerateFocus
record Bag<T>(Either<String, ? extends T> item) {}

"@GenerateFocus: the navigator for 'X.y' cannot navigate to 'T.z'"

X writes generateNavigators = true, its field y holds another @GenerateFocus record, T, and T has a field named like one of the path's own methods that take no arguments. On a FocusPath those are last, head, tail, init, each, some, nullable, cons, snoc, headTail, initLast, segments, pathString, toLens, asAffine, asTraversal, asFold and toPath. An AffinePath and a TraversalPath differ a little, and the processor reads each list from the class the navigator extends.

Fix. Rename the field, or keep y a plain path, as the message says. It names the change that does that on X: add y to excludeFields, remove it from includeFields, or, where includeFields names only y, remove includeFields and set generateNavigators = false. A plain path still reaches the clashing field through .via(TFocus.z()).

Why

A navigator is a path, so it already has each of those methods. A navigation method of the same name would clash with it, and leaving the field out would let name().last() compile against the path's own last(). That method reads the last element of a list, so on a record it throws ClassCastException when the path runs. It is deprecated, and a deprecated method clashes all the same while the path has it. A record that asks for navigators in so many words is refused; one that leaves the default draws a note instead. A name that takes arguments, such as get or count, does not clash, because get() and get(source) are two methods.

A declaration that produces it

@GenerateFocus
record FullName(String first, String last) {}

@GenerateFocus(generateNavigators = true)
record Signatory(FullName name) {}
error: @GenerateFocus: the navigator for 'Signatory.name' cannot navigate to 'FullName.last'. A navigator is a FocusPath, which has its own last(), so SignatoryFocus.name().last() would call that method instead of navigating to the field. Rename 'FullName.last', or add excludeFields = "name" to @GenerateFocus on Signatory to keep 'name' a plain path.

"@GenerateFocus: record component 'X.y' has no navigator into T" (a note)

X leaves navigators to the default, and the record y holds, T, has a field named like one of the path's own methods that take no arguments, the ones the entry before lists. A navigator could not navigate to that field, so y gets none, and its method keeps the path it has without one.

Fix. Rename the field to give y a navigator. To keep the plain path and silence the note, add y to excludeFields on X, as the note says.

Why

A record that leaves the default asked for no navigator in particular, so the field keeps the path it would have with navigators off, rather than failing the build. A record that writes generateNavigators = true is refused instead, by the entry before this one.

A declaration that draws it

@GenerateFocus
record FullName(String first, String last) {}

@GenerateFocus
record Signatory(FullName name) {}
Note: @GenerateFocus: record component 'Signatory.name' has no navigator into FullName. A navigator is a FocusPath, which has its own last(), so it cannot navigate to 'FullName.last', and SignatoryFocus.name() keeps the path it has without one. Rename 'FullName.last' to navigate into FullName, or add excludeFields = "name" to @GenerateFocus on Signatory to keep the path without this note.

"@GenerateFocus: record component 'X.y' has no navigator over its T elements" (a note)

X writes generateNavigators = true, and y is a Map, an array or a third-party collection of @GenerateFocus records that X does not widen, or a container holding one, such as List<Map<String, Item>>. A navigator steps into a container only where the record widens it, so y's method keeps a path to the whole container. If includeFields names y, it draws the includeFields warning instead. Where T has a field named like a method of the widened path, such as last, the note names that field too, since widening y alone would draw the cannot-navigate error.

Fix. Add y to widenFields on X, as the note says, and the method returns a navigator over the elements. Where the note names a field of T, rename it first, or add y to excludeFields to keep the container whole. widenCollections = true widens every such container instead. To keep the container whole otherwise, drop generateNavigators = true, which is the default, and the note goes with it. Whichever you choose, find each call through a field the note names, such as byTicker().last(), by hand: it compiles against the path's own last(), so the compiler cannot point at it.

Why

Widening decides what a field's method focuses on, and a navigator only adds methods to that path, so turning navigators on never changes it. A record that writes generateNavigators = true out may still expect a navigator over a container's elements, so it is told which widenFields value gives one. A record that leaves navigators to the default asked for nothing, and is told nothing.

A call written for a navigator over the elements, such as byTicker().ticker(), does not compile against the container's path, so the compiler points at it. One through a field named like a path method, such as byTicker().last(), compiles against the path's own last() and fails when the path reads. Renaming the field does not change that call.

A declaration that draws it

@GenerateFocus
record Bottle(String label, int volume) {}

@GenerateFocus(generateNavigators = true)
record Hamper(Bottle[] bottles) {}
Note: @GenerateFocus: record component 'Hamper.bottles' has no navigator over its Bottle elements. A navigator steps into a container only where the record widens it, and Hamper does not widen 'bottles'. Add widenFields = "bottles" to @GenerateFocus on Hamper to navigate into its elements.

A declaration that draws it with a field to rename

@GenerateFocus
record Quote(String ticker, double last) {}

@GenerateFocus(generateNavigators = true)
record Book(Map<String, Quote> byTicker) {}
Note: @GenerateFocus: record component 'Book.byTicker' has no navigator over its Quote elements. A navigator steps into a container only where the record widens it, and Book does not widen 'byTicker'. Widened, its navigator would be a TraversalPath, which has its own last(), so it could not navigate to 'Quote.last'. Rename 'Quote.last', then add widenFields = "byTicker" to @GenerateFocus on Book to navigate into its elements, or add excludeFields = "byTicker" to @GenerateFocus on Book to go without this note.

"@GenerateFocus: the navigator for 'X.y' has no TraversalForm" (a note)

X writes generateNavigators = true, and its field y holds another @GenerateFocus record, T. T has a field named like a no-argument method that only a wider path has: headOption or toTraversal on a TraversalPath, or toAffine on an AffinePath. y keeps its own navigator, but not its form for that path, so a hop into y from such a path returns the plain path. The note names AffineForm where that is the form left out. It goes to X, which declares y, since the form belongs to y's navigator; a record whose hop into y returns the plain path is not told.

Fix. Rename the field of T, as the note says, and the navigator gets its form. To keep the name and go without the form, drop generateNavigators = true from X, since it is the default, and the note goes with it.

Why

A form is a navigator over the wider path, so it has that path's methods, and a navigation method of the same name would clash with one. Leaving the field out of the form would let channels().feed().headOption(), from a List<Channel>, compile against the path's own headOption(), which reads the first feed. So the form is left out. It is a note rather than an error because y's own navigator is generated, and every hop from a path of its own type still chains.

A declaration that draws it

@GenerateFocus
record Feed(String name, String headOption) {}

@GenerateFocus(generateNavigators = true)
record Channel(Feed feed) {}
Note: @GenerateFocus: the navigator for 'Channel.feed' has no TraversalForm. A TraversalForm would be a TraversalPath, which has its own headOption(), so it could not navigate to 'Feed.headOption', and a hop into 'feed' from a TraversalPath returns a plain TraversalPath. Rename 'Feed.headOption' to give the navigator for 'feed' a TraversalForm, or remove generateNavigators = true from @GenerateFocus on Channel to go without this note.

"@GenerateFocus: includeFields on X names "y", which is not a component of X"

includeFields, excludeFields or widenFields names a field the record does not have: a typo, or a name a rename left behind. In includeFields it costs the most, since a non-empty list gives navigators only to the fields it names, so a list whose only name is a typo leaves every field without one.

Fix. Write the component's name. Where one is close, the message offers it: a near misspelling, a different case such as "Positions", or an accessor spelling such as "getAddress". Take out a name that is no longer wanted.

Why

The lists hold strings, which javac cannot check against the record, so the processor does. A name that matched nothing would otherwise change nothing, and say nothing about it. The message lists the components it found, so a name with no near miss can be put right from the message alone.

A declaration that produces it

@GenerateFocus
record Kiosk(String town) {}

@GenerateFocus(includeFields = "kisok")
record Hawker(String name, Kiosk kiosk) {}
error: @GenerateFocus: includeFields on Hawker names "kisok", which is not a component of Hawker. includeFields gives navigators only to the components it names, so a name that matches none gives no component a navigator. Did you mean 'kiosk'? Found on Hawker: [name, kiosk]. Replace "kisok" with "kiosk" in includeFields on Hawker.

"@GenerateFocus: includeFields on X names "y", whose field cannot have a navigator" (a warning)

The name matches a component, but that component cannot return a navigator. A String, a List<String> and a record without @GenerateFocus have no navigator target. A List, Set, Collection, Optional or Maybe of @GenerateFocus records has one, as do such containers nested in one another and a Kind the processor traverses. A Map, an array of records or another supported container has one only where the declaring record widens it.

Fix. Name a field that can return a navigator, from those the warning lists. For an unannotated record, annotate its type with @GenerateFocus. For an unwidened container, add the field to widenFields. Where no field can navigate, remove includeFields and set generateNavigators = false. Follow the warning's fix line: leaving includeFields in place with navigators off draws a different warning.

A non-empty includeFields gives navigators only to the fields it names, so naming an ineligible field also takes navigators away from every eligible field it leaves out. The warning points at the offending name and stops a -Werror build. A field already explained by a target note or error gets no additional warning. The unwidened-container note becomes this warning when includeFields names the field.

  • An unwidened Map<String, Box<?>>, with Box<T> annotated, is offered widenFields to step into its Box elements. That alone gives no navigator: the existing wildcard-target note then offers a concrete Box<Object>.
  • A Map<String, ? extends Kiosk> holds Kiosk records, but its wildcard prevents widening. Write Map<String, Kiosk> and add the field to widenFields, as the warning says.
  • A List, Optional or other container the method steps into, holding a record without @GenerateFocus, is offered the annotation, as a field of that record is.
  • When several entries need a replacement, the first in list order receives it and the rest are offered removal. If another entry has a navigator or a correction of its own, remove the invalid name instead. Following all fix lines leaves one list without duplicate names.

A valid name that cannot return a navigator

@GenerateFocus
record Kiosk(String town) {}

@GenerateFocus(includeFields = "id")
record Hawker(String id, Kiosk kiosk) {}
warning: @GenerateFocus: includeFields on Hawker names "id", whose field cannot have a navigator. Navigator generation cannot reach a @GenerateFocus record through String. It needs a @GenerateFocus record or a supported container of one. Replace "id" in includeFields on Hawker with a navigator field from [kiosk].

The offered field gives a navigator:

@GenerateFocus
record Kiosk(String town) {}

@GenerateFocus(includeFields = "kiosk")
record Hawker(String id, Kiosk kiosk) {}

A container that needs widening

@GenerateFocus
record Kiosk(String town) {}

@GenerateFocus(includeFields = "sites")
record Hawker(java.util.Map<String, Kiosk> sites) {}
warning: @GenerateFocus: includeFields on Hawker names "sites", whose field cannot have a navigator. A navigator steps into a container only where the record widens it, and Hawker does not widen "sites". Add widenFields = "sites" to @GenerateFocus on Hawker.

A wildcard container and its combined correction

@GenerateFocus
record Kiosk(String town) {}

@GenerateFocus(includeFields = "sites")
record Hawker(java.util.Map<String, ? extends Kiosk> sites, Kiosk kiosk) {}
warning: @GenerateFocus: includeFields on Hawker names "sites", whose field cannot have a navigator. Map<String, ? extends Kiosk> holds Kiosk records, but its wildcard type arguments prevent widening as written. Write Map<String, Kiosk> in place of Map<String, ? extends Kiosk> and add widenFields = "sites" to @GenerateFocus on Hawker.

Write the concrete type and widen the field together:

@GenerateFocus
record Kiosk(String town) {}

@GenerateFocus(includeFields = "sites", widenFields = "sites")
record Hawker(java.util.Map<String, Kiosk> sites, Kiosk kiosk) {}

var towns = HawkerFocus.sites().town();

A list of a record without the annotation

record Kiosk(String town) {}

@GenerateFocus(includeFields = "sites")
record Hawker(java.util.List<Kiosk> sites) {}
warning: @GenerateFocus: includeFields on Hawker names "sites", whose field cannot have a navigator. Kiosk is a record without @GenerateFocus. Navigator generation cannot reach a @GenerateFocus record through List<Kiosk>. It needs a @GenerateFocus record or a supported container of one. Remove includeFields and set generateNavigators = false in @GenerateFocus on Hawker. Alternatively, annotate record Kiosk with @GenerateFocus.

Annotate the record, and the list navigates into it:

@GenerateFocus
record Kiosk(String town) {}

@GenerateFocus(includeFields = "sites")
record Hawker(java.util.List<Kiosk> sites) {}

var towns = HawkerFocus.sites().town();

"@GenerateFocus: ... on X changes nothing, because ..." (a warning)

A field list is set where the record's other settings never read it, so the generated class is the one you would get without it. There are three such pairs:

  • excludeFields beside a non-empty includeFields, which alone decides which fields get navigators.
  • includeFields or excludeFields with generateNavigators = false, where no field gets a navigator.
  • widenFields beside widenCollections = true, which widens every field widenFields could name.

Fix. Remove the list, or the setting that leaves it unread, as the warning says. Either remedy compiles without the warning.

A declaration that produces it

@GenerateFocus
record Kiosk(String town) {}

@GenerateFocus(includeFields = "home", excludeFields = "relief")
record Trader(String name, Kiosk home, Kiosk relief) {}
warning: @GenerateFocus: excludeFields on Trader changes nothing, because includeFields is set beside it. A non-empty includeFields gives navigators to the components it names and to no others, so excludeFields is never read. Remove excludeFields from @GenerateFocus on Trader, or remove includeFields to give navigators to every component but those excludeFields names.

"@GenerateFocus: widenFields on X names "y", whose T is widened without it" (a warning)

widenFields names a field whose path it cannot change. Either the field's method widens its container anyway, as it does a List, an Optional or an Either, or the field holds no container widenFields widens, such as a String or an int[]. The second reads "which holds no container widenFields widens".

Fix. Take the name out of widenFields, as the warning says. widenFields is for a Map, an array of objects or a third-party collection, which a method otherwise leaves whole. A third-party collection needs a TraversableGenerator on the annotation processor path, such as those hkj-processor-plugins provides, and without one it holds no container widenFields widens; the warning offers that remedy too.

Why

widenFields widens what widenCollections = true widens, one field at a time: a zero-or-more container that a TraversableGenerator on the processor path supports. List, Set, Collection, Optional and Maybe are always widened, since the processor recognises those types, and a zero-or-one container such as Either always is too, so naming one changes nothing. An array of a primitive type is never widened, since EachInstances.arrayEach() is Each<A[], A> and no primitive is an A.

A declaration that produces it

@GenerateFocus(widenFields = "tags")
record Stall(String name, List<String> tags) {}
warning: @GenerateFocus: widenFields on Stall names "tags", whose List<String> is widened without it. StallFocus.tags() returns a TraversalPath whether widenFields names it or not. Remove widenFields from @GenerateFocus on Stall.

"@GenerateTraversals: no traversal was generated for component 'X.y' of type Deque<T>" (a note)

@GenerateTraversals asks the TraversableGenerator SPI for each record component, and no generator on the annotation processor path claimed this one.

Fix. Declare the component as a container a generator supports: List, Set, Collection, Map, Optional, an array, or a type one of the generator plugins covers. For a raw container, give it its element type. For a third-party type, put a TraversableGenerator for it on the annotation processor path. A mixed record, one supported container beside one unsupported, keeps the traversals it can have and carries the note for the one it cannot; the note is the reminder, not a gate. A record that wants no traversal for any of its components should not carry @GenerateTraversals at all; @GenerateLenses on its own still gives every component a lens.

Why

The component unmistakably holds elements, being a java.util.Collection or a java.util.Map by erasure. Not generating for it is a gap rather than the expected outcome, and the generated class would otherwise compile with the method silently missing. The second sentence names the unsupported type (No TraversableGenerator on the annotation processor path supports Deque). The same note is raised, with a different second sentence, for a container a generator did claim but cannot read: a raw List or Set "is written without a type argument, so there is no element type to focus", and a generator whose focused type argument the type does not have says which argument it wanted.

A component that is not a container at all is passed over without comment: a String, an int, or a java.nio.file.Path, which implements Iterable and is why a bare Iterable is not the bar.

It is a note rather than a warning on purpose. @GenerateTraversals has no per-component opt-out, and a processor warning cannot be suppressed, so a warning would have failed every -Werror build with no remedy short of changing the record. A note shows in the compiler output as Note: ... and fails nothing.

A declaration that draws it

@GenerateTraversals
record Backlog(Deque<String> queue) {}

"Multiple TraversableGenerator SPI providers with equal priority (N) support type X" (a warning)

Two generators on the annotation processor path both claim the type, and neither outranks the other: supports() answers true from both at the same priority().

Fix. Rank one of the providers: return PRIORITY_OVERRIDE from the one that should win, or PRIORITY_FALLBACK from the one that should yield, or drop one from the annotation processor path. The message names both provider classes. A consuming build running javac with -Werror turns the warning into an error, so the ranking is the remedy, not optional tidiness. See How Plugin Discovery Works.

Why

Selection is still deterministic, the first registered wins, but which one that is depends on registration order alone, which is what the warning points out. The same warning is raised whichever annotation asks: @GenerateTraversals, @GenerateFocus widening or @ImportOptics. @GenerateFocus raises it once per component.

"@TraverseField: the annotation on record component 'X.y' is not applied" (a note)

@TraverseField names a Traverse for a Kind<F, A> component with a declared witness, and this component is not one.

Fix. Declare the component as the Kind<F, A> the Traverse is written for, Kind<TreeKind.Witness, Tree> for a Traverse<TreeKind.Witness>, with both type arguments given and a witness that is a type rather than a bare or ? super wildcard, a type variable of the record, or a wildcard bounded by one; or drop the annotation and take the path the component gets on its own, widening it yourself with each(EachInstances.fromTraverse(traverse)) where the witness is known. See Custom Kind Types with @TraverseField.

Why

The second sentence says which way: the component is not declared as a Kind at all (List<String> is not declared as a Kind<F, A> component), the Kind is written raw and so names neither a witness nor an element, its witness is a bare or ? super wildcard (Kind<?, String>) that stands for no type and so names no Traverse instance, or its witness is one of the record's own type variables (Kind<F, String> in a Holder<F>, or Kind<? extends F, String>, whose wildcard resolves to F), which stands for any witness, while a Traverse is written for one. The component keeps the path it would have had without the annotation, a plain FocusPath, or .each(EachInstances.listEach()) for a List, which compiles and is correct as far as it goes; what is missing is the traversal the annotation asked for.

It is a note rather than an error because nothing is broken: the generated class is sound, and the same declaration without the annotation passes without comment. A warning cannot be suppressed and would fail a -Werror build with no remedy short of editing the record.

A declaration that draws it

@GenerateFocus
record Inbox(
    @TraverseField(traverse = "org.higherkindedj.hkt.list.ListTraverse.INSTANCE")
        List<String> messages) {}

"@GenerateFocus: record component 'X.y' names a witness the processor does not recognise" (a note)

The component is a Kind<F, A> whose witness is one of Higher-Kinded-J's own, but not one the Focus processor has a Traverse registered for. Nothing widens it, so the generated method is a plain FocusPath focusing the Kind.

Fix. Add @TraverseField naming the Traverse instance for the witness, or keep the plain path and widen it yourself with each(EachInstances.fromTraverse(traverse)). See Kind Field Support.

Why

A witness of your own draws no note, since not traversing it is an ordinary choice; a library witness with no registered Traverse is a gap you would want to hear about. The note is written once for the component, however many navigators reach the record.

A declaration that draws it

@GenerateFocus
record Batch(Kind<NonEmptyListKind.Witness, String> items) {}

@ImportOptics and OpticsSpec interfaces

"@ImportOptics: '...' lists no classes to import, so nothing is generated" (a warning)

An @ImportOptics on a package-info.java, or on a class or interface that does not reach OpticsSpec, names no class to import, so it generates nothing.

Fix. List the classes to import, as @ImportOptics({Order.class}), or remove the annotation. On an interface you meant as a spec, extend OpticsSpec<S> for the type its optics are for.

Why

A warning, because a build that compiles can still be missing the classes it expected, and the first sign would otherwise be a cannot find symbol somewhere else. A processor warning cannot be suppressed, so under -Werror the remedy is the fix. A class or interface whose supertype another processor writes is read once the supertype exists, so the warning is not given before then.

A declaration that produces it

@ImportOptics
class OrderImports {}

"@ImportOptics: '....class' names a primitive type, which has no optics to import"

The class list holds a literal for a primitive type, an array type or void, such as int.class or String[].class. Optics are generated from the declaration of a class, interface, record or enum, and these have none. An array reads names an array type, and void.class reads names void.

Fix. Remove the literal from the list.

A declaration that produces it

@ImportOptics({int.class})
class CountImports {}

"@ImportOptics: '...' extends OpticsSpec<...> and also lists classes to import"

A spec interface also carries a class list. A spec generates the optics its own methods declare, for the type its OpticsSpec<S> names, and does not read a class list.

Fix. Move the class list to a package-info.java or to another class or interface, or remove it.

A declaration that produces it

final class Session {

    public String user() {
        return "";
    }

    public Session withUser(String user) {
        return this;
    }
}

@ImportOptics({java.time.LocalDate.class})
interface SessionOpticsSpec extends OpticsSpec<Session> {

    @Wither(value = "withUser", getter = "user")
    Lens<Session, String> user();
}

"@ImportOptics: '...' extends OpticsSpec only through '...'"

An interface that lists no classes reaches OpticsSpec<S> through another interface rather than declaring it itself. Reading a spec that way is not supported yet.

Fix. Declare OpticsSpec<S> on the spec itself. Where the interface in between names the source type, the message gives the clause to add beside it, extends OpticsSpec<Session>, SessionBase. Where it does not, because it is raw or passes on a type parameter, name the source type in its place: extends OpticsSpec<Session>.

An interface that lists classes, and does not declare OpticsSpec<S> itself, imports them instead, whatever it extends.

A declaration that produces it

final class Session {}

interface SessionBase extends OpticsSpec<Session> {}

@ImportOptics
interface SessionOpticsSpec extends SessionBase {}

"@ImportOptics: '...' implements OpticsSpec but is not an interface"

A class that lists no classes reaches OpticsSpec<S>, directly, through an interface or through a superclass. A spec is an interface: its abstract methods are the optics to generate, and the generated class stands in for it rather than extending it.

Fix. Declare the spec as an interface extending OpticsSpec<S> in place of the class. The message names the clause where it can: Declare it as an interface extending OpticsSpec<Session>.

A declaration that produces it

final class Session {}

@ImportOptics
abstract class SessionOpticsSpec implements OpticsSpec<Session> {}

"@ImportOptics: '...' inherits the optic method '...' from '...'"

A spec interface inherits a method that returns an optic, abstract or default, from another interface. A spec generates optics from the methods it declares itself, and reading them from another interface is not supported yet, so the generated class would be missing that optic.

Fix. Declare the method on the spec itself, annotated as it is on the interface it comes from. The message writes the signature for the spec's own source type, Lens<Order, UUID> id() for a mix-in's Lens<S, UUID> id(); reached through a raw clause, it asks for the type arguments instead. For a default method, redeclare it on the spec as an abstract method with its copy strategy or hint annotation, which replaces the inherited one. Or move the default out of the mix-in into a static method that calls the generated statics. A method the spec redeclares is its own, and the one it inherits is no longer read.

Why

Leaving the method out would be quieter and worse: code calling it would fail with cannot find symbol, and a @ThroughField traversal composing through an inherited lens would fail inside the generated file. A spec extending another @ImportOptics spec draws it too, since its own generated class would lack the other's optics. An inherited method that returns something other than an optic, such as int count(), is not a declaration of an optic and leaves the spec as it is. So does one taking arguments or declaring type parameters of its own, which a spec could not declare as an optic either.

A declaration that produces it

final class Session {

    public String user() {
        return "";
    }

    public Session withUser(String user) {
        return this;
    }
}

interface UserOptics {

    @Wither(value = "withUser", getter = "user")
    Lens<Session, String> user();
}

@ImportOptics
interface SessionOpticsSpec extends OpticsSpec<Session>, UserOptics {}

"@ImportOptics: Lens method 'x' carries no copy strategy annotation"

A method on an OpticsSpec interface returns Lens<S, A> but carries none of @Wither, @ViaConstructor, @ViaCopyAndSet or @ViaBuilder, so the processor has no way to know how the external type rebuilds itself.

Fix. Add the appropriate hint based on how the source type is copied. See Optics for External Types and Database Records with JOOQ for the full strategy table.

A declaration that produces it

final class Session {

    public String user() {
        return "";
    }
}

@ImportOptics
interface SessionOpticsSpec extends OpticsSpec<Session> {

    Lens<Session, String> user();
}

"'XOpticsSpec.foo' is a default method"

A spec interface declares a default method. A method body cannot be read during annotation processing, so there is nothing for the generated class to carry.

Fix. Keep the spec interface to annotated abstract methods. Composed optics belong in a static method on the interface, or in an ordinary utility class; either one calls the generated statics by name, for example JsonNodeOptics.object().andThen(...).

A declaration that produces it

final class Session {

    public String user() {
        return "";
    }
}

@ImportOptics
interface SessionOpticsSpec extends OpticsSpec<Session> {

    default Lens<Session, String> user() {
        return Lens.of(Session::user, (session, user) -> session);
    }
}

"'XOpticsSpec' declares OpticsSpec<S>, which is a type variable"

The spec interface is generic, and its own type parameter is the source type: interface SessionOpticsSpec<S extends Session> extends OpticsSpec<S>.

Fix. Name the type the optics are for as the type argument, with its own type arguments where it has any: OpticsSpec<Session>. Where the bound names a single type that is not raw, the message suggests it for you. A source type that is itself generic is supported, and the spec names its own type parameters: interface BoxOpticsSpec<U> extends OpticsSpec<Box<U>> generates static <U> Lens<Box<U>, U> value(). See Spec Interfaces for which parameters a generated method declares. It is only a bare type variable, standing for the whole source type, that has no source to read.

Why

Optics are generated against one named type, read for its members and rebuilt through its constructor, wither or setter, so a type parameter standing for whatever a caller picks has nothing to generate from. An array source type produces the same diagnostic with a different opening, declares OpticsSpec<String[]>, which is an array type, and the same remedy.

A declaration that produces it

class Session {}

@ImportOptics
interface SessionOpticsSpec<S extends Session> extends OpticsSpec<S> {

    @Wither("withUser")
    Lens<S, String> user();
}

"'XOpticsSpec' declares OpticsSpec<Box>, which names the raw type 'Box'"

The source type names a generic type without its arguments.

Fix. Name the raw type's arguments in the OpticsSpec clause: OpticsSpec<Box<String>>, OpticsSpec<Outer<String>.Holder>, OpticsSpec<Box<List<String>>>. A spec whose optics should stay generic declares its own type parameters and passes them on, interface BoxOpticsSpec<U> extends OpticsSpec<Box<U>>. See Spec Interfaces.

Why

Every generated optic repeats the source type verbatim, so the generated file, which you cannot edit, would carry a [rawtypes] warning that the @SuppressWarnings on your own spec does not cover, and a @ViaConstructor rebuild read under a raw type erases its parameters into an [unchecked] call besides. Three shapes draw the error: the source type itself written bare (OpticsSpec<Box> for a Box<X>), a member type behind a generic outer written bare (OpticsSpec<Outer.Holder>, raw by JLS 4.8 even though Holder declares nothing of its own), and a raw type argument (OpticsSpec<Box<List>>). Raw is not the same as bare: a non-generic source type, or a static nested type of a generic outer, has no arguments to supply and is accepted as written. A record component is different, and the optics generated for one carry that suppression for you: a component is the declared shape of your data, while an OpticsSpec clause is a signature written for the generator, where the type arguments are yours to supply. The focus type a spec method declares is the same kind of thing as a component, and its optic method carries the suppression too.

A declaration that produces it

record Box<T>(T value) {

    Box<T> withValue(T value) {
        return new Box<>(value);
    }
}

@ImportOptics
interface BoxOpticsSpec extends OpticsSpec<Box> {

    @Wither("withValue")
    Lens<Box, String> value();
}

"@ImportOptics: type '...' names the type parameter 'T' of '...', which hides the 'T' of its enclosing class '...'"

An inner class imported by class literal declares a type parameter under a name its enclosing class already uses.

Fix. Import it through a spec interface, which names the type under type parameters of its own, interface InOpticsSpec<A, B> extends OpticsSpec<Outer<A>.In<B>>, and give each field a @Wither lens. See Generic Spec Interfaces.

Why

An inner class of a generic class is imported under its enclosing class's type parameters, because without them the type would be raw: @ImportOptics({Outer.In.class}) names Outer<X>.In<Y> and declares both parameters on each generated method. Inside In, a parameter named like Outer's hides it, which is harmless there, but one method cannot declare two type parameters with the same name. A static nested class has no enclosing instance type and takes only its own parameters, so it never draws this.

A class read from a jar draws it too, but its compiled signatures cannot tell the two parameters apart and read both as the inner one. For such a class the spec names them as one: OpticsSpec<Outer<A>.In<A>>.

A declaration that produces it

class Outer<T> {

    final class In<T> {

        private final T item;

        In(T item) {
            this.item = item;
        }

        public T item() {
            return item;
        }

        public In<T> withItem(T item) {
            return new In<>(item);
        }
    }
}

@ImportOptics({Outer.In.class})
class OuterImports {}

"@ThroughField: '...' reaches field 'items', which is declared as ArrayList<String> rather than as the List interface"

The spec's own lens for the field focuses something narrower than a container interface: a concrete container such as ArrayList, or another interface such as Deque. Auto-detection matches List, Set, Collection, Map, Optional and reference-type arrays, on the interface itself.

Fix. Name a traversal that rebuilds the declared type, Traversals.forIterableCollecting(ArrayList::new) for a list-shaped container or Traversals.forMapValuesCollecting(TreeMap::new) for a map, exposed as a static method and named fully qualified: @ThroughField(field = "items", traversal = "com.example.MyTraversals.forArrayList()"). Where the type is yours, declaring the field as the interface (List<String>) is the simpler route. See @ThroughField auto-detection.

Why

The type the message names is that lens focus. Each standard traversal promises no more than the interface type (Traversals.forList() hands back an unmodifiable List), and the composed optic writes that value back into the field through the lens; a field declared as something narrower, a concrete container (ArrayList, HashSet, TreeMap) or another interface (Deque, SortedSet), cannot take it, so the generated traversal would throw ClassCastException on first use, on a read as well as a write. The message names the interface the field's type implements. An array of a primitive (int[]) draws the sibling message: the array traversal walks an Object[], which an int[] is not.

A declaration that produces it

final class Shelf {

    public ArrayList<String> items() {
        return new ArrayList<>();
    }

    public Shelf withItems(ArrayList<String> items) {
        return this;
    }
}

@ImportOptics
interface ShelfOpticsSpec extends OpticsSpec<Shelf> {

    @Wither("withItems")
    Lens<Shelf, ArrayList<String>> items();

    @ThroughField(field = "items")
    Traversal<Shelf, String> eachItem();
}

"@ThroughField: '...' composes through a lens named 'items', which the spec does not declare"

A @ThroughField traversal is generated as the spec's own lens for the field composed with the container traversal, Spec.items().andThen(...). The spec has to declare that Lens<S, F> items() alongside it, with its copy strategy, and this one does not. Without the lens the generated file could only fail with cannot find symbol, so the processor refuses the declaration instead.

Fix. Declare the lens method for the field on the spec, or use @TraverseWith to name a traversal over the source type that stands on its own.

A lens declared raw, Lens items(), reads as which the spec declares raw. It is refused for the same reason: the traversal composes onto what the lens focuses, and a raw lens says nothing about that. Declare it with both type arguments, as Lens<Sack, List<String>>.

A lens declared static or private reads as which the spec declares static or private. A method with a body stays on the spec, and the traversal composes through the generated class's lens, so declare items as an abstract Lens method with its copy strategy.

A declaration that produces it

final class Shelf {

    public List<String> items() {
        return List.of();
    }
}

@ImportOptics
interface ShelfOpticsSpec extends OpticsSpec<Shelf> {

    @ThroughField(field = "items")
    Traversal<Shelf, String> eachItem();
}

"@ThroughField: '...' declares focus 'Integer' over field 'items' of type List<String>, whose elements the standard traversal hands back as 'String'"

The method's declared focus does not contain what the auto-detected traversal hands back. That is the container's elements, a Map's values, an Optional's element, or Object where the element sits behind a super- or unbounded wildcard.

Fix. Declare the focus as the type the message names, or name a traversal of your own with @ThroughField(field = "items", traversal = "..."), which is the author's undertaking that it rebuilds the declared shape.

Why

A focus that does not contain that type could only compile through a cast, throwing ClassCastException on the caller's first getAll or modify where it narrows, and letting ill-typed writes into the container where it widens. Containment, not sameness: a wildcard focus over the element (? extends CharSequence over CharSequence elements, or its element's supertype bound) stays accepted, and an extends-wildcard element is held to its bound (List<? extends CharSequence> hands back CharSequence).

A declaration that produces it

final class Shelf {

    public List<String> items() {
        return List.of();
    }

    public Shelf withItems(List<String> items) {
        return this;
    }
}

@ImportOptics
interface ShelfOpticsSpec extends OpticsSpec<Shelf> {

    @Wither("withItems")
    Lens<Shelf, List<String>> items();

    @ThroughField(field = "items")
    Traversal<Shelf, Integer> eachItem();
}

"@InstanceOf target 'com.example.Foo' is not a subtype of source type 'com.example.Base'"

The class passed to @InstanceOf(SubType.class) is not a subclass of the optic's source type.

Fix. Verify that SubType extends or implements the spec's <S> parameter. If you are working with sum types that don't use a sealed hierarchy (such as Jackson's pre-3.x JsonNode), use @MatchWhen with predicate and getter method names instead.

A declaration that produces it

sealed interface Payment permits Card, Bank {}

record Card(String pan) implements Payment {}

record Bank(String iban) implements Payment {}

@ImportOptics
interface PaymentOpticsSpec extends OpticsSpec<Payment> {

    @InstanceOf(String.class)
    Prism<Payment, String> text();
}

"@InstanceOf: '...' tests 'Grid', which no '...' can be"

The class @InstanceOf names is a subtype of the source's class, but its own declaration writes a different type where the source type writes one, so no value of the source would pass the test.

Fix. Name a class in @InstanceOf that the source type can be, or declare the prism on a spec of its own over the type the class declares, which the message names: Node<Y[]> for a Grid<Y>. NodeOpticsSpec keeps the source its other methods were written for:

class Node<T> {}

class Grid<Y> extends Node<Y[]> {}

class Counts extends Node<int[]> {}

@ImportOptics
interface NodeOpticsSpec extends OpticsSpec<Node<int[]>> {

    @InstanceOf(Counts.class)
    Prism<Node<int[]>, Counts> counts();
}

@ImportOptics
interface GridOpticsSpec<Y> extends OpticsSpec<Node<Y[]>> {

    @InstanceOf(Grid.class)
    Prism<Node<Y[]>, Grid<Y>> grid();
}

Why

A type argument has to be exactly the type the source writes in its place. A Grid<Y> is a Node<Y[]>, which is a Node<int[]> only with Y as int, and a type argument is never a primitive. The same holds wherever no choice of the class's parameters makes the type it declares the source type. A Box<X> extends Node<List<X>> is never a Node<Set<String>>, nor a Node<List<?>>, since no List<X> is exactly the type List<?>. A Twin<X> extends Node<Pair<X, X>> is never a Node<Pair<String, Integer>>. The prism would never match, so one of the two declarations is the mistake, and the processor says so at the spec method. A wildcard the source writes as one of its own arguments admits any type within its bounds, so only a type outside them is refused: no Node<? extends Number> is a Node<Object>. A type variable in the source could stand for any class, so it rules nothing out unless a primitive stands against it.

A declaration that produces it

class Node<T> {}

class Grid<Y> extends Node<Y[]> {}

@ImportOptics
interface NodeOpticsSpec extends OpticsSpec<Node<int[]>> {

    @InstanceOf(Grid.class)
    Prism<Node<int[]>, Node<int[]>> grid();
}

"@InstanceOf: '...' declares its focus as Circle<T>, which the test cannot narrow to"

The prism promises a type argument the test cannot check.

Fix. Declare the focus as Circle<?>, which is what the test earns, or narrow through a predicate and getter of the source type with @MatchWhen, which reads the argument off the source rather than inventing it. Where the source type does carry the argument, as in a Circle<X> implements Shape<X> reached from Shape<T>, the prism may promise it and the generated test names it. See Spec Interfaces.

Where what the test earns is no source type, no focus would build the source back: a Box<?> is no Node<U> for Box<X> extends Node<List<X>>. The message then offers another class, or the prism on a spec of its own over Node<List<X>>, as for a test that would never match.

Why

@InstanceOf takes a class constant, which is raw, and the generated instanceof runs after erasure, so the only arguments the narrowed value is known to have are the ones the source type pins down. class Circle<X> extends Shape reached from a Shape that declares no parameters pins none: every instantiation passes the same test, and a Prism<Shape, Circle<T>> would hand any of them back as the T the caller asked for, to fail on the first read.

An inner class of a generic class is narrowed the same way, its enclosing class's arguments included. From a source that pins nothing of Outer's, Outer.Plain.class earns Outer<?>.Plain, so a focus of Outer<String>.Plain draws this message.

A declaration that produces it

class Shape {}

class Circle<X> extends Shape {}

@ImportOptics
interface ShapeOpticsSpec<T> extends OpticsSpec<Shape> {

    @InstanceOf(Circle.class)
    Prism<Shape, Circle<T>> circle();
}

"@InstanceOf: '...' tests 'Outer.Inner', where Y extends X and '...' pins nothing to Outer's X"

A parameter the test pins is bounded by a parameter the source pins nothing to. A bound that only names it, such as Y extends List<X>, reads as List<?>, and the argument the source pins has to be within it, as an argument outside its bound explains.

Fix. Narrow through a predicate and getter of the source type with @MatchWhen, or write the prism by hand with Prism.of. Where you can change the classes, a source that pins X as well lets the test name it.

Why

The test writes a wildcard wherever the source pins nothing, so Outer.Inner.class narrowed from a Node<U> is tested as Outer<?>.Inner<U>. A bound of X on Inner's parameter then has nothing to name, and javac refuses that type: U is not within the bound of a wildcard it cannot see. A permitted subtype of a sealed interface draws the same refusal from @GeneratePrisms, as does not bind shows.

A declaration that produces it

class Node<U> {}

class Outer<X> {

    class Inner<Y extends X> extends Node<Y> {}
}

@ImportOptics
interface NodeOpticsSpec<U> extends OpticsSpec<Node<U>> {

    @InstanceOf(Outer.Inner.class)
    Prism<Node<U>, Node<U>> inner();
}

"@InstanceOf: '...' tests '...', where Y extends Number and U is not a 'Number'"

The source pins a parameter of the class @InstanceOf names to an argument outside that parameter's bound.

Fix. The message ends in one of two lines:

  • It declares a spec parameter where one bound will do. The declaration, as in interface NodeOpticsSpec<U extends Number>, keeps every bound the parameter has, so it can replace the one there.
  • Otherwise it offers another class for @InstanceOf. The message still lists every bound the argument has to meet, so a spec that can take them all may declare them itself, as in U extends Number & Comparable<U>.

Either way, @MatchWhen narrows through a predicate and getter of the source type instead.

Why

The test names the type a matching value must be, so Gauge.class narrowed from a Node<U> is tested as Gauge<U>. javac refuses a type argument outside its parameter's bound, and with Y extends Number and nothing said about U, the generated file would not compile. No Node<U> can be a Gauge unless U is a Number, which is why a bound on the spec is the fix. javac reads a parameter the source leaves free as the ? the test writes for it. In class Rack<A, B extends List<A>> extends Node<B>, the argument the source pins to B has to be a List<?>.

A declaration that produces it

class Node<U> {}

class Gauge<Y extends Number> extends Node<Y> {}

@ImportOptics
interface NodeOpticsSpec<U> extends OpticsSpec<Node<U>> {

    @InstanceOf(Gauge.class)
    Prism<Node<U>, Node<U>> gauge();
}

"@InstanceOf: '...' narrows to '...', which is not a '...'"

The class the annotation names is not one the prism's focus type accepts.

Fix. Name the class the focus declares, or declare the focus as a supertype of the narrowed type. A prism whose focus is deliberately wider than the test is fine, as in @InstanceOf(ArrayList.class) Prism<Collection<T>, List<T>>; it is only a focus the narrowed value cannot be assigned to that is rejected. Where the narrowed type is no source type, no supertype of it would do. The message then offers another class, the focus's own where that is a source type, or a spec of its own over the type the narrowed class declares.

Why

Either the two are unrelated, or the source type pins the target's argument to something the focus does not agree with: OpticsSpec<Node<String>> narrowed to Leaf can only be a Leaf<String>, whatever a Prism<Node<String>, Leaf<U>> says.

A declaration that produces it

sealed interface Payment permits Card, Bank {}

record Card(String pan) implements Payment {}

record Bank(String iban) implements Payment {}

@ImportOptics
interface PaymentOpticsSpec extends OpticsSpec<Payment> {

    @InstanceOf(Card.class)
    Prism<Payment, Bank> card();
}

"@ViaCopyAndSet: copyConstructor names '...', which does not resolve to a type"

copyConstructor is a plain string, resolved as a fully qualified class name only: it is not read against the spec interface's imports, and it takes no type arguments.

Fix. Give the class's fully qualified name (com.example.BaseConfig; a nested class is com.example.Outer.Base), the class alone without type arguments, since the processor supplies those from the source type's own extends clause. Drop the attribute to pass the source unchanged.

A declaration that produces it

// Endpoint is the legacy type from the copy-strategies page: two copy constructors,
// taking BaseEndpoint and Audited, and a setHost setter.
@ImportOptics
interface EndpointOpticsSpec extends OpticsSpec<Endpoint> {

    @ViaCopyAndSet(copyConstructor = "com.example.MissingBase", setter = "setHost")
    Lens<Endpoint, String> host();
}

"@ViaCopyAndSet: copyConstructor names '...', which 'S' does not extend or implement"

The generated setter passes the source to the copy constructor as (ParameterType) source, so only a supertype of S can be named there.

Fix. Name a class or interface S extends or implements, or drop the attribute.

A declaration that produces it

@ImportOptics
interface EndpointOpticsSpec extends OpticsSpec<Endpoint> {

    @ViaCopyAndSet(copyConstructor = "java.lang.Thread", setter = "setHost")
    Lens<Endpoint, String> host();
}

"@ViaCopyAndSet: copyConstructor names '...', which is not public and so cannot be named from '...'"

The generated optics class has to write the cast, so it has to be able to name the type. A package-private supertype is invisible from the package the optics class is generated into, even though new S(source), which never names it, would have compiled.

Fix. Name a public supertype, generate into that package with @ImportOptics(targetPackage = ...), or drop the attribute.

"@ViaCopyAndSet: copyConstructor names '...', which '...' reaches as '...', and no constructor accepts"

The name is a genuine supertype, but no single-argument constructor of S takes the type S actually reaches it as. java.lang.Object and marker interfaces such as Serializable reach this often.

Fix. Name a supertype of S that one of the listed constructors takes, as the class alone without type arguments, or drop the attribute. The list carries type arguments and the attribute does not, so read it to recognise your supertype in it rather than to copy from it, and a listed type that is not a supertype of S cannot be named at all. The attribute is only needed when the copy constructor is overloaded; see Copy Strategies.

Why

The message names both the type you gave and the one S reaches, which differ when S's own extends clause pins the arguments: class PNode<X> extends PBase<String> reaches PBase as PBase<String>, whatever X is.

A declaration that produces it

@ImportOptics
interface EndpointOpticsSpec extends OpticsSpec<Endpoint> {

    @ViaCopyAndSet(copyConstructor = "java.lang.Object", setter = "setHost")
    Lens<Endpoint, String> host();
}

"@ViaCopyAndSet: '...' is written with a wildcard type argument"

The source type carries a wildcard, OpticsSpec<Node<?>>, and the strategy rebuilds it through a constructor.

Fix. Name the type the wildcard stands for, or switch to @Wither or @ViaBuilder, which rebuild through methods and name no constructor, so a wildcard source type is no obstacle there. For an abstract class or an interface, name a concrete class as the spec's source type, or switch to @Wither or @ViaBuilder likewise. Every other refusal's remedy offers only those two strategies for such a source type.

Why

new Node<?>(...) is not something that can be written, whatever the arguments. @ViaConstructor reports the same thing for the same reason. An inner class draws the sibling message, because its constructor call needs an enclosing instance the generated class has no way to reach. So do an abstract class and an interface, which cannot be instantiated at all.

A declaration that produces it

final class Slot<T> {

    private String label = "";

    Slot() {}

    Slot(Slot<T> other) {
        this.label = other.label;
    }

    public String label() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }
}

@ImportOptics
interface SlotOpticsSpec extends OpticsSpec<Slot<?>> {

    @ViaCopyAndSet(setter = "setLabel")
    Lens<Slot<?>, String> label();
}

"@ViaCopyAndSet: The generated lens returns the copy it calls '...' on, and drops what '...' returns" (a warning)

The setter is spelt as a wither, withTitle, and may return the source type, but not every method of that name the new value can be passed to returns it. One returns a supertype, a raw type, void, or a type that holds the source type, such as an Optional. So the lens calls it for its effect and hands back the copy. A wither leaves the copy as it was, and then set changes nothing.

Fix. Name a setter that sets the copy, or rebuild the source type with @ViaBuilder or @ViaConstructor. Where @Wither would rebuild through the method the focus binds, the warning offers it in place of naming another setter. Where the method does set the copy it is called on, as a fluent withTitle returning its base type does, annotate the lens method @SuppressWarnings("dropped-result"), or the spec interface to cover each lens it declares.

Why

A setter spelt as a wither is taken at its word, and the lens hands back what it returns. That needs the result to be the source type whichever method of that name the call binds, so every method the new value can be passed to counts. A call that binds one returning no source type drops nothing, and does not warn. So a void withTitle(Object) beside a withTitle(String) that returns the source type warns, though a String binds the second. So does a retag whose type the call infers from its argument, such as <U> Memo<U> withId(U). A set method is never taken at its word, since one that returns the source type may hand back the value it replaced, so it never warns.

For this declaration, the warning reads The lens can hand back the result only where every method of that name the new value can be passed to returns a 'Hedge', and 'withTitle(String)' returns 'Shrub'.

A declaration that produces it

class Shrub {

    protected String title = "";

    public Shrub withTitle(String title) {
        Shrub copy = new Shrub();
        copy.title = title;
        return copy;
    }
}

final class Hedge extends Shrub {

    Hedge() {}

    Hedge(Hedge other) {
        this.title = other.title;
    }

    public String getTitle() {
        return title;
    }
}

@ImportOptics
interface HedgeOpticsSpec extends OpticsSpec<Hedge> {

    @ViaCopyAndSet(setter = "withTitle")
    Lens<Hedge, String> getTitle();
}

"@Wither: '...' returns '...', not the source type '...'"

The method a spec's @Wither call binds hands back something other than the source type the spec declares: that type raw, the type under other arguments, or a supertype.

Fix. Name a wither that returns the source type. Where the wither returns the type under fixed arguments, Memo<String> withId(String) on a Memo<T>, declare the spec over that instantiation, OpticsSpec<Memo<String>>, and it serves as it is. Otherwise rebuild the source type with @ViaBuilder or @ViaConstructor, or with @ViaCopyAndSet through a setter that sets the copy it is called on. The same wither named as that setter has its new value dropped, and draws the dropped-result warning. See Copy Strategies.

Why

The generated lens sets through the wither and hands its result back as the source type. A raw return gets there only by an unchecked conversion, in a generated file your own @SuppressWarnings does not reach, and any other type does not get there at all: Base<String>, inherited by a Sub extends Base<String>, is not a Sub. The wither is read on the source type as the spec names it, which is why the fixed-argument case above works, and a retag, <U> Memo<U> withId(String), is accepted because the call infers U back to the spec's argument. A variable that only a wildcard argument stands for, as in a spec over Memo<? extends Number>, is not inferred, and such a wither is refused. A wither that returns the class's own parameter is not one of those: S withName(String) on a Shape<S extends Shape<S>> rebuilds a Shape<?>.

Where the name is overloaded, the method checked is the one the call binds. The lens passes the new value typed by its focus, and javac chooses among the overloads by that type, so beside a withN(int) that returns the source type, an Object withN(Integer) is the one an Integer focus calls, and the one refused. Where that choice rests on inference, a method whose parameter is one of its own type variables or one taking varargs, the name is accepted when one of the methods the call might bind is an instance method returning the source type, and javac checks the call itself. A focus written as a wildcard is read the same way, since such a lens infers its focus from the getter as much as from the wildcard. Imported by class literal instead, @ImportOptics({Memo.class}), such a wither is not paired at all, under the pairing rule.

A declaration that produces it

final class Memo<T> {

    private final String id;

    Memo(String id) {
        this.id = id;
    }

    public String id() {
        return id;
    }

    @SuppressWarnings("rawtypes")
    public Memo withId(String id) {
        return new Memo<>(id);
    }
}

@ImportOptics
interface MemoOpticsSpec<T> extends OpticsSpec<Memo<T>> {

    @Wither(value = "withId", getter = "id")
    Lens<Memo<T>, String> id();
}

"@Wither: '...' has no method '...' for the generated lens to call"

The method a spec's @Wither names is not one the generated class can call on the source type: the name is misspelt, or the method is private, or package-private in another package. Where the name is declared but out of reach, the message says so rather than claiming the type has no such method.

Fix. Correct the name. The message lists the source type's withers, its one-parameter instance methods that hand it back, with the value each one takes. It offers the with method you likely meant: one a near miss away, or one naming the same value, withId for id or setId. Where the type has none, rebuild it with @ViaBuilder, @ViaConstructor or @ViaCopyAndSet. See Copy Strategies.

Why

The generated lens sets through source.withX(newValue), so the method has to exist, declared or inherited, where the class generated beside your spec can reach it. The name is checked at the spec method rather than left to javac, whose cannot find symbol would point into the generated file.

A declaration that produces it

final class Licence {

    private final String id;

    Licence(String id) {
        this.id = id;
    }

    public String id() {
        return id;
    }

    public Licence withId(String id) {
        return new Licence(id);
    }
}

@ImportOptics
interface LicenceOpticsSpec extends OpticsSpec<Licence> {

    @Wither(value = "withIdentifier", getter = "id")
    Lens<Licence, String> id();
}

"@Wither: No method '...' of '...' takes the lens's focus type '...'"

The source type has methods of the name @Wither gives, and none of them takes the value the lens sets, whose type is the lens's focus. Where none takes one argument, the name is usually an accessor's, id written for withId, and the message offers the wither for that value. The @ViaBuilder and @ViaCopyAndSet messages offer the setter for that value the same way, in the strategy's convention.

Fix. Name a wither that takes the value the getter reads, or point getter at an accessor one of the listed methods takes and declare the focus as its type. The focus alone rarely settles it: the getter pins it from below, so a focus wide enough to read the getter is usually too wide for a parameter the getter's own type already failed. The exception is a focus wider than the wrapper of a primitive getter, Number over an int one, which is never unboxed: there the message offers the wrapper, Integer, as the focus to declare. Otherwise rebuild the source type with @ViaBuilder, @ViaConstructor or @ViaCopyAndSet. Where the parameter is a type the source type's wildcard stands for, the T of withValue(T) on a spec over Cell<?>, declare the spec over the type the wildcard stands for, a type parameter of the spec if need be: interface CellOpticsSpec<T> extends OpticsSpec<Cell<T>>.

Why

The generated setter passes the new value to the wither as the focus type, so a method taking another type cannot be called with it. A parameter that a wildcard argument of the source type stands in takes no value at all, since the type it stands for is unknown, which is why that case is fixed on the source type rather than the focus. A lens whose focus is a wildcard is not checked here: javac infers such a focus from the getter as much as from the wildcard, so which method the call binds is left to javac.

A declaration that produces it

final class Tally {

    private final String count;

    Tally(String count) {
        this.count = count;
    }

    public String count() {
        return count;
    }

    public Tally withCount(Integer count) {
        return new Tally(String.valueOf(count));
    }
}

@ImportOptics
interface TallyOpticsSpec extends OpticsSpec<Tally> {

    @Wither(value = "withCount", getter = "count")
    Lens<Tally, String> count();
}

"@Wither: The generated call to '...' cannot choose between '...' and '...'"

More than one overload of the wither takes the lens's focus, and none takes it more closely than the rest, so javac would report the generated call as ambiguous.

Fix. Declare the lens's focus as the parameter type of the overload you mean. Below, a Lens<Ident, CharSequence> reaches only withId(CharSequence), and the getter's String is still a CharSequence.

Why

javac calls the overload whose parameter is the most specific of those the argument fits. A String fits both Serializable and CharSequence, and neither of those is a subtype of the other, so there is no most specific one. Naming the focus as one of the parameter types leaves that overload the only one the argument fits.

A declaration that produces it

final class Ident {

    private final String id;

    Ident(String id) {
        this.id = id;
    }

    public String id() {
        return id;
    }

    public Ident withId(java.io.Serializable id) {
        return new Ident(id.toString());
    }

    public Ident withId(CharSequence id) {
        return new Ident(id.toString());
    }
}

@ImportOptics
interface IdentOpticsSpec extends OpticsSpec<Ident> {

    @Wither(value = "withId", getter = "id")
    Lens<Ident, String> id();
}

"@Wither: '...' is static, so the generated lens cannot rebuild a '...' through it"

The overload of the wither that the lens's focus binds is a static method.

Fix. Declare the lens's focus as the parameter type of an instance overload, which is what binds it instead; or name an instance wither; or rebuild the source type with @ViaBuilder, @ViaConstructor or @ViaCopyAndSet.

Why

The generated lens calls the wither on the value it sets, and a static method never reads that value, so whatever it builds keeps none of the fields it was not passed. The call is checked among every method of the name, static ones included, because javac weighs them all: here a String binds the static withId(CharSequence) ahead of the instance withId(Object).

A declaration that produces it

final class Token {

    private final String id;

    Token(String id) {
        this.id = id;
    }

    public String id() {
        return id;
    }

    public static Token withId(CharSequence id) {
        return new Token(id.toString());
    }

    public Token withId(Object id) {
        return new Token(String.valueOf(id));
    }
}

@ImportOptics
interface TokenOpticsSpec extends OpticsSpec<Token> {

    @Wither(value = "withId", getter = "id")
    Lens<Token, String> id();
}

"'...' has no method '...()' for the generated lens to read the value it focuses"

Every copy strategy reads with the same lambda, source -> source.getX(). @Wither and @ViaBuilder name that accessor in their getter; @ViaCopyAndSet and @ViaConstructor read through the lens method's own name. The name here is not a zero-parameter instance method the generated class can call: it is misspelt, static, takes an argument, or is out of reach.

The same shape of message covers every name a strategy calls with no argument, each ending on what the generated lens wanted it for:

Ends onThe name that is missing
for the generated lens to read the value it focusesthe getter, or the lens method that stands in for it
for the generated lens to rebuild through@ViaBuilder's toBuilder
for the generated lens to finish the value it rebuilds@ViaBuilder's build, read on what the setter hands back
for the generated lens to read the argument '...'an accessor named in @ViaConstructor's parameterOrder

Fix. Point the strategy's attribute at a method the type declares. The message offers one where the name is a near miss, or the same accessor under another naming convention, however many letters apart: getName for name, name for getName, isActive for getActive. On a protobuf-java message it offers the getter that reads a repeated or map field whole, getTagsList for tags or getTags, and getPointsMap for points. No offer is ever a deprecated method, such as a map field's getPoints(), since the generated file's call to one fails a build under -Werror. Where the strategy has no such attribute, name the lens method after the accessor it reads.

Why

The generated optic calls the name as written. Checked here, the spec method is where the mistake is reported, rather than cannot find symbol inside a file you did not write. A static method is not one, since the lens calls it on the value it focuses; nor is one that takes an argument, since the lens passes none.

A declaration that produces it

final class Permit {

    private final String id;

    Permit(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public Permit withId(String id) {
        return new Permit(id);
    }
}

@ImportOptics
interface PermitOpticsSpec extends OpticsSpec<Permit> {

    @Wither(value = "withId", getter = "getIdentifier")
    Lens<Permit, String> id();
}

"'...()' reads '...', not the lens's focus '...'"

The accessor exists, and reads a value of another type. LocalDate is the standard example: withMonth takes an int, while getMonth() reads a Month, so the two do not make a lens between them.

Fix. Point the strategy's getter at an accessor that reads the focus, getMonthValue() for that pair, or declare the lens over what this one reads and rebuild through a method that takes it. Where the strategy reads through the lens method's own name, rename the lens method.

Why

A lens reads and writes one type. The getter pins it from below, so a focus the getter can fill and the wither can take has to be a type they share; where they share none, no focus makes the pair work, and the mismatch is better reported here than as an inference failure in generated code. A primitive accessor reads into its wrapper, int into Integer, so that pair is not a mismatch.

A declaration that produces it

enum Month { JANUARY, FEBRUARY }

final class Renewal {

    private final int month;

    Renewal(int month) {
        this.month = month;
    }

    public Month getMonth() {
        return Month.values()[month - 1];
    }

    public int getMonthValue() {
        return month;
    }

    public Renewal withMonth(int month) {
        return new Renewal(month);
    }
}

@ImportOptics
interface RenewalOpticsSpec extends OpticsSpec<Renewal> {

    @Wither(value = "withMonth", getter = "getMonth")
    Lens<Renewal, Integer> month();
}

Unchecked reads and rebuilds through a raw type

The external class declares an accessor, a builder or a supertype raw, or the spec writes the focus raw. The generated lens would read or rebuild through it in a way javac flags as unchecked. These messages report it, under the strategy's own tag:

The message saysWhat it means
'getItems()' reads the raw type 'List', which reaches the lens's focus 'List<String>' only through an unchecked conversionThe accessor reads a raw type, or a class that extends one, and the lens hands it back as a focus with type arguments
'withTyped(List<String>)' takes the lens's focus, the raw type 'List', only through an unchecked conversion to 'List<String>'The focus is written raw, and the method the lens rebuilds through takes the type with its arguments. For @ViaConstructor it reads takes what 'getItems()' reads where an accessor's value goes to the constructor
'toBuilder()' hands back the raw type 'Docket.Builder', so the generated call to 'id(T)' on it is uncheckedThe builder comes back raw, and erasing it changes a parameter of the setter: a type variable such as T, or a type with arguments such as List<String>
'Doc' inherits 'setValue(T)' from the raw type 'Base', so the generated call to it is uncheckedThe source type extends a raw type, and the method called is one it inherits from there
'build()' returns the raw type 'Tote', which reaches the source type 'Tote<String>' only through an unchecked conversionThe builder chain turns raw at some step, so build() returns the erasure of the source type
'items(List<T>)' takes a type that names what a wildcard of 'Bin<?>' stands forThe focus is written raw, and the method takes the type a wildcard stands for, in the source or in the builder toBuilder() hands back

Fix. Read and rebuild through members that name their types with arguments: an accessor that reads List<String>, a method that takes it, a builder whose steps hand back Builder<T>. Where the class offers only raw members, declare the lens over the raw type they read, Lens<Shelf, List>, with @SuppressWarnings("rawtypes") on the lens method. Where a wildcard of the source stands in the parameter, declare the spec with a type argument in its place and the focus as the type the method then takes: interface BinOpticsSpec<T> extends OpticsSpec<Bin<T>> with Lens<Bin<T>, List<T>>. Where toBuilder() hands the wildcard back, point toBuilder at a method that hands back the builder with its type arguments. Otherwise rebuild with another strategy.

Why

A raw type is not refused in itself. A raw focus is your own declaration, and the optic generated for it carries the suppression, as it does for a record component. What is refused is generated code that converts a raw value to a type with arguments, or calls a method through a raw type that erases one of its parameters. javac flags both as unchecked in the generated file, which you cannot edit, and the @SuppressWarnings on your spec does not reach it. Under -Xlint:unchecked that is a warning, which -Werror turns into a failed build; without it, javac still notes that the file uses unchecked operations. A setter that erasure leaves as it is, such as name(String), is called through a raw builder cleanly, so a chain through it that builds a type without arguments is accepted. Nor is a raw value refused where the type it reaches is reifiable, such as List<?> or Class<?>, which javac converts to with no warning. A List<T> read on a Builder<?> is not one of those: javac reads it as a list of the type the wildcard stands for, which a raw value reaches only unchecked. An accessor that declares a type variable of its own, such as <T extends List> T items(), is accepted too, since javac infers its type at the call.

A declaration that produces it

final class Shelf {

    private final List items;

    Shelf(List items) {
        this.items = items;
    }

    public List getItems() {
        return items;
    }

    public Shelf withItems(List items) {
        return new Shelf(items);
    }
}

@ImportOptics
interface ShelfOpticsSpec extends OpticsSpec<Shelf> {

    @Wither(value = "withItems", getter = "getItems")
    Lens<Shelf, List<String>> items();
}

"'...' has no method '...' for the generated lens to set through"

The setter a @ViaBuilder or @ViaCopyAndSet names is not a method of the type it is called on: the builder for the first, the source type for the second.

Where the name exists, the call is held to what a wither's is, under the strategy's own tag: takes the lens's focus type where no overload takes the focus, cannot choose between where more than one does, and is static, so the generated lens cannot set through it where the one it binds is static, since a static method never reads the value it is called on. Where the focus is a wildcard and no method of the name takes one argument, the message says takes one argument instead. On a spec over Bag<?>, a toBuilder() declared to hand back a Builder<List<T>> hands back a builder of lists of the one type the wildcard stands for. No focus the spec can write is such a list. The message then says A parameter that a wildcard of 'Bag<?>' stands in takes no value at all, and lists the builder's methods as toBuilder() declares them. The fix is the wither's: declare the spec over the type the wildcard stands for. Where the wildcard is the builder's own, as in a toBuilder() that hands back a Builder<?>, point toBuilder at a method that hands back the builder with its type arguments.

Fix. Set the strategy's setter to a method that type declares, and one that takes the value the getter reads. The message offers one where the name is a near miss, or names the same value in the strategy's convention. Under @ViaBuilder, it offers a fluent builder's name, a protobuf builder's setName or a withName step, as the builder declares them. For a protobuf repeated or map field it offers the addAllName or putAllName that sets the field whole. Since the setter defaults to the lens method's name, a lens named getName is offered name. Under @ViaCopyAndSet, it offers only a set method, near miss or not. A bare or with method may hand back a copy, and @Wither is the strategy for one of those.

Why

@ViaBuilder sets through source.toBuilder().setter(newValue).build() and @ViaCopyAndSet through a copy, so the setter is looked for where the call is made rather than on the source type in both cases. A builder's setter that returns void ends the chain, which the chain entry covers.

A declaration that produces it

final class Server {

    private String host;

    Server() {}

    Server(Server other) {
        this.host = other.host;
    }

    public String host() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }
}

@ImportOptics
interface ServerOpticsSpec extends OpticsSpec<Server> {

    @ViaCopyAndSet(setter = "setHostname")
    Lens<Server, String> host();
}

@ViaBuilder: the chain the lens rebuilds through

@ViaBuilder rebuilds with source.toBuilder().setter(newValue).build(), and each step is called on what the step before it hands back. Two messages report a chain that does not lead back to the source type: 'toBuilder()' hands back 'int', which is not a builder to set through, and 'build()' returns 'String', not the source type 'Waybill'. A setter that returns void reads hands back 'void', which is not a builder to build from. A subclass whose toBuilder() comes from its parent reads 'Dog' inherits 'toBuilder()' from 'Animal', whether the parent's builder lacks the setter or builds an Animal.

Fix. Name the methods the type's own builder declares. A Lombok @SuperBuilder subclass needs toBuilder = true of its own, since the toBuilder() it inherits builds its parent. Where the type has no such chain, rebuild it with @Wither, @ViaConstructor or @ViaCopyAndSet.

Why

A builder chain is three calls, and only the first is made on the source type. Reading each step off the one before it is what catches a build() that hands back the builder, or a setter that returns nothing, before the generated file does. Each step is read as javac reads the call. So for the PetBuilder<?, ?> that Lombok's @SuperBuilder(toBuilder = true) hands back for a Pet, the setter returns a PetBuilder, and build() a Pet. A step whose type is bounded by more than one type, such as B extends PetBuilder<B> & Serializable, is read on the bound that declares the next call. Where unrelated bounds both declare it, which one the call binds is left to javac. Where none declares it, the message names them all, hands back a type bounded by 'Tag.Base' and 'Tag.Texts', which have no method 'txet', and offers a near miss from any.

A declaration that produces it

final class Waybill {

    private final String id;

    Waybill(String id) {
        this.id = id;
    }

    public String id() {
        return id;
    }

    public Builder toBuilder() {
        return new Builder();
    }

    static final class Builder {

        private String id;

        public Builder id(String id) {
            this.id = id;
            return this;
        }

        public String build() {
            return id;
        }
    }
}

@ImportOptics
interface WaybillOpticsSpec extends OpticsSpec<Waybill> {

    @ViaBuilder
    Lens<Waybill, String> id();
}

@ViaConstructor: the order the lens rebuilds through

@ViaConstructor rebuilds with new S(source.a(), newValue, source.c()): the accessor each name in the order reads, and the new value where the lens method's own name stands. The order is parameterOrder, or the one the constructor's parameter names give when you leave it out. These messages report an order the generated call cannot rebuild through:

The message saysWhat it means
'parameterOrder' names no argument for the lens's own 'id'The order never names the lens method, so the rebuild would set nothing
'parameterOrder' passes 'source.y()' where 'Point(int x, int y)' takes 'x'A value goes where a parameter named after another accessor stands
No constructor of 'Sum' takes a parameter named after the lens's own 'total()'Every parameter is named after another accessor, so no place takes the lens's value; 'Heat(double kelvin)' takes no parameter when a written order binds such a constructor
No constructor of 'Label' takes (String)No constructor takes the arguments: too few, too many, or a type out of place
cannot choose between 'Duo(int a, Integer b)' and 'Duo(Integer a, int b)'More than one constructor takes them, none more closely than the rest
No 'parameterOrder' is written, and no constructor ofWith no order written, no constructor's names give one; more than one constructor when two give different orders
binds '...', not a constructor it was read fromThe order read from one constructor's names binds another constructor
leaves out what '...' takesThe order read from the names is a shorter constructor's, beside a longer one; for a record, beside its canonical constructor

Fix. Where the constructor's parameter names give an order the call would rebuild through, the message ends on it, ready to paste: @ViaConstructor(parameterOrder = {"x", "y"}). Where the lens's focus is what no constructor takes, it names the type the getter reads, to declare as the focus instead. Where the names give no order, or the order read from them would rebuild through the wrong constructor, write the order of the one the lens should rebuild through. Where no constructor takes the lens's value, or javac cannot choose one, rebuild the source type with @Wither, @ViaBuilder or @ViaCopyAndSet. A lens named after an alias, price() for a parameter cents, can take the accessor's own name instead. A parameter can be named after an accessor that reads something else, as Money(long amount, ...) beside an amount() that reads pounds rather than pence. The processor still holds the order to that name, and the lens needs another strategy.

Why

A constructor call checks only types. Point(int x, int y) takes new Point(source.y(), newValue) as readily as the right order, and the lens then writes each value into the other's field. Setting x to 9 on a Point(1, 2) gives a Point(2, 9), so a get after the set no longer reads 9. Only the parameter names tell the two apart. So a parameter named after an accessor that reads a value it takes, x after x(), getX() or isX(), has to be passed that accessor's value. A parameter named after nothing the type reads leaves its place to the order. A variable-arity constructor is held the same way: its fixed parameters by their names, and every value passed into its array by the array parameter's.

A constructor compiled from source always carries its names. One read from a class file carries them when it was compiled with -parameters or -g, and Gradle and Maven pass -g by default. Without either, javac reads the names as arg0 and arg1, which name no accessor, so the order you write is held to the types alone, and one left out cannot be read. A record's canonical constructor is read by its components' names, which a class file always keeps.

A declaration that produces it

final class Point {

    private final int x;
    private final int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int x() {
        return x;
    }

    public int y() {
        return y;
    }
}

@ImportOptics
interface PointOpticsSpec extends OpticsSpec<Point> {

    @ViaConstructor(parameterOrder = {"y", "x"})
    Lens<Point, Integer> x();
}

An order one argument short:

final class Label {

    private final String id;
    private final String tag;

    Label(String id, String tag) {
        this.id = id;
        this.tag = tag;
    }

    public String id() {
        return id;
    }

    public String tag() {
        return tag;
    }
}

@ImportOptics
interface LabelOpticsSpec extends OpticsSpec<Label> {

    @ViaConstructor(parameterOrder = {"id"})
    Lens<Label, String> id();
}

Two constructors that each take two Integers only by unboxing one of them:

final class Duo {

    private final Integer a;
    private final Integer b;

    Duo(int a, Integer b) {
        this.a = a;
        this.b = b;
    }

    Duo(Integer a, int b) {
        this.a = a;
        this.b = b;
    }

    public Integer a() {
        return a;
    }

    public Integer b() {
        return b;
    }
}

@ImportOptics
interface DuoOpticsSpec extends OpticsSpec<Duo> {

    @ViaConstructor(parameterOrder = {"a", "b"})
    Lens<Duo, Integer> a();
}

A swap among a variable-arity constructor's fixed parameters, which says passes 'source.note()' where 'Sticker(String label, String note, String... parts)' takes 'label':

final class Sticker {

    private final String label;
    private final String note;
    private final String[] parts;

    Sticker(String label, String note, String... parts) {
        this.label = label;
        this.note = note;
        this.parts = parts.clone();
    }

    public String label() {
        return label;
    }

    public String note() {
        return note;
    }

    public String[] parts() {
        return parts.clone();
    }
}

@ImportOptics
interface StickerOpticsSpec extends OpticsSpec<Sticker> {

    @ViaConstructor(parameterOrder = {"note", "label"})
    Lens<Sticker, String> label();
}

No order written, for a lens no constructor takes a parameter for:

final class Sum {

    private final int x;
    private final int y;

    Sum(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int x() {
        return x;
    }

    public int y() {
        return y;
    }

    public int total() {
        return x + y;
    }
}

@ImportOptics
interface SumOpticsSpec extends OpticsSpec<Sum> {

    @ViaConstructor
    Lens<Sum, Integer> total();
}

"@ViaConstructor: 'parameterOrder' ... passes nothing for '...', which '...' takes" (a warning)

A written order rebuilds through the constructor its call binds, even one that takes fewer values than the source holds. Beside Fee(long amount, String currency), {"amount"} binds Fee(long amount), and every set then hands back a Fee in that constructor's default currency. The warning names every value a longer constructor takes that the order passes nothing for, from each longer constructor; for a record, every component.

Fix. Pass it: where a constructor's names give an order that passes it, the warning offers that order, ready to paste, @ViaConstructor(parameterOrder = {"amount", "currency"}). A shorter constructor can work the value out on purpose: a Rect(int w, int h) works out the area a Rect(int w, int h, int area) record holds. Then annotate the lens method @SuppressWarnings("shorter-constructor"), or the spec interface to cover each lens it declares. Where no one constructor takes all the values, as Knot(int x, int y) and Knot(int x, long z) take one each, no order passes them all, and the warning says so rather than offer one.

Why

After every set, a value the order leaves out reads what the shorter constructor gives it, not what the source held. Setting amount to 9 on a Fee(100, "EUR") gives a Fee(9, "GBP"), so setting a value the lens has just read changes the source, which a lens may not do. A variable-arity constructor passed nothing for its array is shorter in the same way: {"label"} for Sticker(String label, String... parts) leaves parts empty.

Only the parameter names say which values a longer constructor takes, so a parameter named after nothing the type reads never counts. Two constructors of one length, such as Temperature(double celsius) and Temperature(float fahrenheit), are two views of one value rather than a shorter and a longer one. A longer view still warns, as Amount(long pounds, int pence) beside Amount(long cents) does, and the warning says that no order can pass its values with the lens's own. It is a warning rather than an error because a constructor that works a value out keeps the lens lawful, and only you know which of yours do.

For this declaration, the warning reads 'parameterOrder' {"amount"} passes nothing for 'currency()', which 'Fee(long amount, String currency)' takes.

A declaration that produces it

final class Fee {

    private final long amount;
    private final String currency;

    Fee(long amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    Fee(long amount) {
        this(amount, "GBP");
    }

    public long amount() {
        return amount;
    }

    public String currency() {
        return currency;
    }
}

@ImportOptics
interface FeeOpticsSpec extends OpticsSpec<Fee> {

    @ViaConstructor(parameterOrder = {"amount"})
    Lens<Fee, Long> amount();
}

"@ImportOptics: '...' pairs more than one wither with the field '...'" (a note)

A class imported by class literal spells one field's accessor more than one way, and has a wither for each: int n() with withN(int), and String getN() with withN(String). Both pair with the field n, and a class can carry only one lens of that name.

Fix. Nothing, where the lens you wanted is the one generated: the note says which. Where you wanted the other, import the type through a spec interface and name the pair yourself with @Wither(value = "withN", getter = "getN"), importing it there rather than by class literal, or the note stays on every build.

Why

The pairing rule tries the accessor spellings in order, n(), then getN(), then isN(), and the first that matches is the one generated. Reading them in that order rather than in the order the class happens to declare its members means the same class always yields the same lens. A note rather than a warning, because the class is someone else's to spell, and a note does not fail a -Werror build.

A declaration that produces it

public final class Counter {

    private final int n;

    public Counter(int n) {
        this.n = n;
    }

    public int n() {
        return n;
    }

    public String getN() {
        return String.valueOf(n);
    }

    public Counter withN(int n) {
        return new Counter(n);
    }

    public Counter withN(String n) {
        return new Counter(Integer.parseInt(n));
    }
}

@ImportOptics({Counter.class})
class CounterImports {}

"@ImportOptics: '...' focuses '...', which is not a '...'"

A prism runs both ways, and the generated one builds back with identity: it returns the value it narrowed.

Fix. Focus the variant that carries the value, TextNode rather than String, and read the payload with a further optic. Where the value type is the point, write that prism by hand with Prism.of and a build side that constructs the source, such as TextNode::valueOf. Where what an @InstanceOf test earns is no source type, no focus could take it and still be one. The message then offers another class, or a spec of its own over the type the class declares.

Why

That is only a source when the focus is one, so a focus that is a value rather than a variant has no build side the processor could write: Prism<JsonNode, String> would need to rebuild a JsonNode from a bare String, and nothing in the declaration says how. The requirement belongs to the prism rather than to either hint, so @InstanceOf and @MatchWhen are both held to it. That includes an @InstanceOf whose narrowing is sound but reaches the focus through a supertype the source does not share, Prism<Base, Marker> for a Sub implements Base, Marker.

A declaration that produces it

sealed interface Payment permits Card, Bank {}

record Card(String pan) implements Payment {}

record Bank(String iban) implements Payment {}

@ImportOptics
interface PaymentOpticsSpec extends OpticsSpec<Payment> {

    @MatchWhen(predicate = "isCard", getter = "pan")
    Prism<Payment, String> pan();
}

"cannot find symbol", inside the generated XPrisms.java, after using @MatchWhen

The processor does not validate the strings in @MatchWhen(predicate = "isFoo", getter = "asFoo"). It splices them into the generated source verbatim, so a typo surfaces as an ordinary javac error inside generated code rather than as a processor message.

Fix. Check the names against the source type's API. Both methods must take no arguments; the predicate returns boolean and the getter returns the prism's target type.

A declaration that produces it

sealed interface Payment permits Card, Bank {

    default boolean isCard() {
        return this instanceof Card;
    }

    default Card asCard() {
        return (Card) this;
    }
}

record Card(String pan) implements Payment {}

record Bank(String iban) implements Payment {}

@ImportOptics
interface PaymentOpticsSpec extends OpticsSpec<Payment> {

    @MatchWhen(predicate = "isCrad", getter = "asCard")
    Prism<Payment, Card> card();
}

"Prism method 'x' requires a prism hint annotation: @InstanceOf or @MatchWhen"

A spec-interface method returning Prism<S, A> with neither hint.

Fix. Add @InstanceOf for a real subtype, or @MatchWhen for a check-and-extract API. The same rule applies to traversals: "Traversal method 'x' requires a traversal hint annotation: @TraverseWith or @ThroughField".

A declaration that produces it

sealed interface Payment permits Card, Bank {}

record Card(String pan) implements Payment {}

record Bank(String iban) implements Payment {}

@ImportOptics
interface PaymentOpticsSpec extends OpticsSpec<Payment> {

    Prism<Payment, Card> card();
}

@GeneratePathBridge and @PathVia

Every message on this page is quoted as the processor emits it, with 'x' standing in for the name it prints.

The bridge is a file you never wrote and cannot edit, so the errors below refuse a shape at your own declaration rather than emitting source that would fail, or warn, in the build that consumes it. The last entry is a warning rather than an error: the bridge it describes is written, it just has nothing in it.

"@PathVia: the return type of 'x' is 'Y', which no Path wraps"

The method returns a type the bridge has no Path for. The bridged set is Optional, Maybe, Either, Try, Validated and IO; CompletableFuture is the type most often met outside it.

Fix. Return one of the six, or drop @PathVia and wrap the call by hand.

A declaration that produces it

@GeneratePathBridge
interface Orders {

    @PathVia
    CompletableFuture<String> find(String id);
}

"@PathVia: the signature of 'x' names the raw type 'Y'"

A generic type is written without its arguments somewhere the bridge copies verbatim: Optional as the return type, Optional<List> as its argument, List as a parameter. Each becomes a [rawtypes] warning in the generated file, and the @SuppressWarnings on your own declaration does not cover a file it does not appear in.

Fix. Name the type arguments: Optional<Item> rather than Optional. A raw bound on the interface's own type parameter is not refused: the bridge repeats it in its class declaration and carries @SuppressWarnings("rawtypes") there.

A declaration that produces it

@GeneratePathBridge
interface Orders {

    @PathVia
    Optional find(String id);
}

"@PathVia: the error type of the 'Validated' returned by 'x' is the wildcard '?'"

A Validated bridge names its error type twice: in the ValidationPath it returns, and in the Semigroup it asks the caller for.

Fix. Name the error type.

Why

A wildcard is a different captured type at each mention, so no argument satisfies both.

Only the error position is affected. Validated<String, ? extends Number> is fine, Validated<List<? extends CharSequence>, String> is fine because the wildcard is nested and denotes one type at both mentions, and so are wildcards in Optional, Maybe, Either and Try returns.

A declaration that produces it

@GeneratePathBridge
interface Orders {

    @PathVia
    Validated<?, String> find(String id);
}

"@PathVia: the type parameter 'T' on 'x' has the same name as 'Y's"

The bridge declares the interface's type parameters and the method's side by side, which the delegate never does; where the names collide, the method's hides the interface's.

Fix. Rename the method's type parameter.

Why

An inherited <T extends U> on a Derived<T> would be written <T extends T>, and a parameter typed by the interface's T would silently become the method's.

Only a collision the signature actually depends on is refused. <T> Optional<T> get(T t) on a Derived<T> names nothing it hides, and is generated unchanged.

A declaration that produces it

interface Narrower<U> {

    @PathVia
    <T extends U> Optional<T> narrow(T candidate);
}

@GeneratePathBridge
interface TextNarrower<T> extends Narrower<T> {}

"@PathVia: the bridge cannot call 'x'"

The method is static or private. The bridge reaches its delegate through an interface reference, which gets at abstract and default members and nothing else.

Fix. Make it an abstract or default instance method, or drop @PathVia from it.

A declaration that produces it

@GeneratePathBridge
interface Orders {

    @PathVia
    static Optional<String> find(String id) {
        return Optional.empty();
    }
}

"@PathVia: the bridge signature for 'x' is already taken"

Two @PathVia methods land on the same generated name and parameter types, usually through @PathVia(name = ...). One class cannot declare both.

Fix. Give one of them a distinct name, or drop @PathVia from it.

A declaration that produces it

@GeneratePathBridge
interface Orders {

    @PathVia
    Optional<String> find(String id);

    @PathVia(name = "find")
    Optional<String> lookup(String id);
}

"@PathVia: @PathVia(name = "...") is not a method name"

The name attribute is not a Java identifier, or it is a keyword. The bridge declares a method called exactly that.

Fix. Give a plain identifier, or drop the attribute to keep the delegate's own name.

A declaration that produces it

@GeneratePathBridge
interface Orders {

    @PathVia(name = "find-order")
    Optional<String> find(String id);
}

"@GeneratePathBridge: on 'X', the signature names 'Y', which cannot be reached from 'p'"

targetPackage puts the bridge in package p, and something the bridge writes down, a parameter type, a return type, a bound or the delegate itself, is not visible there. The same message names the bound on 'T' when the culprit is a type parameter's bound.

Fix. Make public the class the message names, which may be one enclosing the type. Where the type sits beside the interface, removing targetPackage also works, since the bridge is then written there, and the message offers it where it does.

A declaration that produces it

class Secret {}

@GeneratePathBridge(targetPackage = "com.example.paths")
interface Vault {

    @PathVia
    Optional<Secret> find(String id);
}

"@GeneratePathBridge: no @PathVia method was found among 'X's members" (a warning)

No @PathVia method survives among the interface's members, so the bridge is written with a constructor and nothing else.

Fix. Put @PathVia on the methods to bridge, or drop @GeneratePathBridge.

Why

Usually that means none was ever written; it can also mean one was hidden, which the note below covers.

Inherited methods do count: a bridge for StringStore extends Store<String> picks up Store's, read under String. But @PathVia is not inherited by an override, so a method that overrides an annotated one hides it unless it is annotated too, and that is the usual cause of this message on an interface whose parent is annotated.

A processor warning cannot be suppressed, so a build running -Werror treats this as an error.

A declaration that draws it

@GeneratePathBridge
interface Orders {

    Optional<String> find(String id);
}

Focus DSL chains

traverseOver and the higher-kinded witness type

traverseOver takes its witness type from the Traverse argument, but its element type only from where the result goes. In a chain such as .traverseOver(...).getAll(user), the element type falls back to Object, and javac reports incompatible types: List<Object> cannot be converted to List<Role>.

Fix. Widen through each(EachInstances.fromTraverse(...)), which takes the element type from the Kind the path focuses on, so it holds in a chain too:

TraversalPath<Operator, Role> allRoles =
    rolesPath.each(EachInstances.fromTraverse(ListTraverse.INSTANCE));

Why

traverseOver's element type appears only in its return type, so Java can infer it only from a target type, such as the variable the result is assigned to. The phantom error type in Effect §1 is inferred the same way. The receiver of a further method call is not a target, so the element type takes its bound, Object. fromTraverse returns an Each typed from the Kind it reads, as every Each is, so the path's own focus fixes the element. traverseOver is deprecated for removal in 0.6.0. Focus DSL Reference shows the failing chain beside the fix.

"Incompatible types when chaining .each().via()"

Usually one .each() too many.

Fix. Drop the extra .each(), and break long chains into intermediate variables so each carries a concrete type:

TraversalPath<OrgChart, OrgUnit>   units    = OrgChartFocus.units();
TraversalPath<OrgChart, OrgMember> members  = units.via(OrgUnitFocus.members());
TraversalPath<OrgChart, Integer>   salaries = members.via(OrgMemberFocus.salary());

Why

A generated accessor for a collection component is already element-level, so OrgChartFocus.units() is a TraversalPath<OrgChart, OrgUnit> and adding .each() steps into an OrgUnit as though it were a list. Long chains can also overflow Java's inference budget.

The extra .each() compiles

The untyped each() is <E> TraversalPath<S, E> and infers E from the assignment target, so a surplus hop type-checks and then fails at runtime when the list traversal is applied to something that is not a list. It is deprecated for removal in 0.6.0. Its typed form, each(EachInstances.listEach()), does not compile on a focus that is not a List.

"Cannot infer type argument(s)" on an intermediate .each()

Only the final untyped each() in a chain can infer its element type from the target type. An intermediate one has nothing to infer from.

Fix. Use the typed form at each hop. It takes the element type from the List the path focuses on, so no hop needs it spelled out:

TraversalPath<OrgChart, Integer> allSalaries =
    FocusPath.of(OrgChartLenses.units())
        .each(EachInstances.listEach())
        .via(OrgUnitLenses.members())
        .each(EachInstances.listEach())
        .via(OrgMemberLenses.salary());

"No suitable method found for each(...)" on a typed step

The path does not focus on the container the step names: EachInstances.listEach() takes a path that focuses on a List, and FocusPaths.optionalSome() one that focuses on an Optional.

Fix. Give the step the instance for what the path does focus on. A generated collection path already focuses on each element, so it needs no step of its own, and a Set, a Map or another container takes its own Each. A path over a List<? extends T> takes an Iso to List<T> first, and one over a Kind<F, ? extends T> an Iso to Kind<F, T>, as 0.6.0 deprecation migration shows.

// Rejected: the generated path already focuses on each OrgUnit, not on a List
TraversalPath<OrgChart, OrgUnit> units = OrgChartFocus.units().each(EachInstances.listEach());

Why

listEach() is an Each<List<A>, A>, and each takes an Each whose first type is the path's focus, so javac looks for an A that makes List<A> an OrgUnit and finds none. The deprecated no-argument each() compiled here, because it does not read its element type from the focus, and failed with ClassCastException when it ran. some(FocusPaths.optionalSome()) on a focus that is not an Optional, and via(FocusPaths.listAt(0)) on one that is not a List, give the same message, naming some or via.

"each() in org.higherkindedj.optics.focus.FocusPath has been deprecated and marked for removal" (a warning)

A Focus path's no-argument each(), some(), head(), last(), tail(), init(), cons(), snoc(), headTail() or initLast() is deprecated for removal in 0.6.0, and so are its at(index), atKey(key) and traverseOver(traverse). The warning names the method and the path type, a navigator included.

Fix. Write its typed form: the same verb, given an instance that names the container, such as each(EachInstances.listEach()) or some(FocusPaths.optionalSome()). Removals in 0.6.0 gives each one. The MigrateDeprecationsTo0_6_0 recipe rewrites the calls it can and marks the rest, as 0.6.0 deprecation migration says.

// Warned: each() takes its element type from the assignment, not from the path
TraversalPath<OrgChart, OrgUnit> units = FocusPath.of(OrgChartLenses.units()).each();

Why

The shortcut does not read its element type from the path's focus. So it compiles on any focus, and fails with ClassCastException when it runs on one that is not its container. The typed form reads the element from the focus, so on the wrong focus it does not compile, with No suitable method found for each(...). The warning fails a build only under -Werror.

"Method reference ::new doesn't work with single-field records as BiFunction"

A single-component record has no two-argument constructor, and Lens.of's setter is a BiFunction<S, A, S> taking (source, newValue). It is an arity mismatch, not an inference wobble.

Fix. Use an explicit lambda:

Lens<ShelfLabel, Barcode> lens = Lens.of(ShelfLabel::barcode, (label, code) -> new ShelfLabel(code));

"Sealed or non-sealed local classes are not allowed"

Defining a sealed interface inside a method body. Java does not permit this regardless of HKJ.

Fix. Hoist the sealed interface to class or top level.


Free Monad DSL programs

"Cannot resolve method 'flatMap(Function<...>)'"

The Free<F, A> value's witness type does not match what the surrounding interpreter expects, or you are mixing Free<OpticOpKind.Witness, ...> with another Free instance.

Fix. Confirm that every step in the program uses the same OpticPrograms factory methods, and that interpreter calls are paired with the matching witness.

"Type mismatch: Free<F, A> cannot be converted to A"

Forgetting to call an interpreter. A Free program is data; you must run it to get a result.

Fix. Pass the program to an interpreter:

Saver result = OpticInterpreters.direct().run(program);

When the message does not match anything here

  1. Is the project rebuilt from clean? Many "cannot find symbol" errors clear after ./gradlew clean build.
  2. Is the annotation processor on the classpath? See Build Plugins for the canonical setup.
  3. Is the IDE indexing the generated sources directory? Refresh the project after a build.
  4. If it is none of these, please file an issue at the Higher-Kinded-J GitHub repository with the minimal reproducer and the full error.

See Also


Previous: Focus DSL Reference