Summary
Every fluent builder method on Http.ClientBuilder<T> is uncallable in a chain from outside the com.duosecurity.client package. Any chain such as the documented new Admin.AdminBuilder(...).useTimeout(30).build() fails to compile with:
build() is defined in an inaccessible class or interface
(and similarly for any method invoked after a fluent setter).
Root cause
ClientBuilder is declared protected, but its fluent setters are typed to return the ClientBuilder<T> base type rather than a public self-type:
Http.java:424 — protected abstract static class ClientBuilder<T extends Http> {
Http.java:455 — public ClientBuilder<T> useTimeout(int timeout)
Http.java:478 — public ClientBuilder<T> useMaxBackoffMs(long maxBackoffMs)
Http.java:493 — public ClientBuilder<T> useCustomCertificates(String pemContent)
Http.java:506 — public ClientBuilder<T> disableCaPinning()
Http.java:519 — public ClientBuilder<T> addAdditionalDuoHeader(String name, String value)
Http.java:534 — public ClientBuilder<T> addHeader(String name, String value)
Http.java:544 — public T build()
The public subclass Admin.AdminBuilder overrides only createClient, not the setters, so the setters keep the ClientBuilder<T> return type.
Although useTimeout/build/etc. are public, in Java a member is only accessible when the static type of the receiver expression is also accessible. The constructor expression new Admin.AdminBuilder(...) has the public type AdminBuilder, so calling .build() directly on it compiles. But the moment any setter is called, the expression's static type becomes the protected ClientBuilder<Admin>, and every subsequent call — another setter or build() — references a member declared in an inaccessible class, hence the error.
The library's own tests don't hit this because they live in com.duosecurity.client, where protected members are accessible. Any consumer in a different package hits it on the first chained setter.
Reproduction
From a package other than com.duosecurity.client:
Admin admin = new Admin.AdminBuilder("GET", host, uri)
.useTimeout(30)
.build(); // does not compile: build() is defined in an inaccessible class or interface
Workaround
Ignore the setters' return values and keep operating on the AdminBuilder-typed reference:
Admin.AdminBuilder builder = new Admin.AdminBuilder("GET", host, uri);
builder.useTimeout(30);
builder.disableCaPinning();
Admin admin = builder.build();
Suggested fix
Either make ClientBuilder public, or (preferably) give the fluent setters a proper self-type so chaining returns the concrete public builder — e.g. a recursive generic bound (ClientBuilder<T extends Http, B extends ClientBuilder<T, B>> returning B), or override the setters in each concrete builder (AdminBuilder, AuthBuilder, AccountsBuilder) with covariant return types.
This issue was generated with AI assistance (Claude).
Summary
Every fluent builder method on
Http.ClientBuilder<T>is uncallable in a chain from outside thecom.duosecurity.clientpackage. Any chain such as the documentednew Admin.AdminBuilder(...).useTimeout(30).build()fails to compile with:(and similarly for any method invoked after a fluent setter).
Root cause
ClientBuilderis declaredprotected, but its fluent setters are typed to return theClientBuilder<T>base type rather than a public self-type:Http.java:424—protected abstract static class ClientBuilder<T extends Http> {Http.java:455—public ClientBuilder<T> useTimeout(int timeout)Http.java:478—public ClientBuilder<T> useMaxBackoffMs(long maxBackoffMs)Http.java:493—public ClientBuilder<T> useCustomCertificates(String pemContent)Http.java:506—public ClientBuilder<T> disableCaPinning()Http.java:519—public ClientBuilder<T> addAdditionalDuoHeader(String name, String value)Http.java:534—public ClientBuilder<T> addHeader(String name, String value)Http.java:544—public T build()The public subclass
Admin.AdminBuilderoverrides onlycreateClient, not the setters, so the setters keep theClientBuilder<T>return type.Although
useTimeout/build/etc. arepublic, in Java a member is only accessible when the static type of the receiver expression is also accessible. The constructor expressionnew Admin.AdminBuilder(...)has the public typeAdminBuilder, so calling.build()directly on it compiles. But the moment any setter is called, the expression's static type becomes theprotectedClientBuilder<Admin>, and every subsequent call — another setter orbuild()— references a member declared in an inaccessible class, hence the error.The library's own tests don't hit this because they live in
com.duosecurity.client, whereprotectedmembers are accessible. Any consumer in a different package hits it on the first chained setter.Reproduction
From a package other than
com.duosecurity.client:Workaround
Ignore the setters' return values and keep operating on the
AdminBuilder-typed reference:Suggested fix
Either make
ClientBuilderpublic, or (preferably) give the fluent setters a proper self-type so chaining returns the concrete public builder — e.g. a recursive generic bound (ClientBuilder<T extends Http, B extends ClientBuilder<T, B>>returningB), or override the setters in each concrete builder (AdminBuilder,AuthBuilder,AccountsBuilder) with covariant return types.This issue was generated with AI assistance (Claude).