Changelog
4.22.3
(fix): Honor package-prefix and package-layout for local-file-system output when the CLI
requests a full project (e.g. --generate-tests, --package, or self-hosted SDKs).
Previously these were dropped when converting the local-file-system config, so the root
package fell back to com.<org>.<api> instead of the configured prefix. Full-project local
output now also writes sources at their full package path (src/main/java/com/acme/...)
instead of stripping the prefix from the directory.
4.22.2
(fix): RetryInterceptor now applies the client timeout to each attempt individually instead of to the
whole retry loop. Previously a Retry-After wait as long as the timeout (e.g. Retry-After: 60
with the default 60s timeout) cancelled the call during the backoff sleep and surfaced
IOException: Canceled instead of retrying or returning the received 429. The call timeout is
paused while waiting for the backoff and restarted for each retry attempt, so a call that retries
may now take up to timeout * (maxRetries + 1) plus the backoff waits.
4.22.1
(fix): Closing the root client (or ClientOptions.close()) now disconnects any WebSocket clients that are still
connected before the SDK-owned OkHttp dispatcher is shut down, so they stop reconnecting cleanly instead of
failing asynchronously with InterruptedIOException: executor rejected. ClientOptions exposes isClosed();
WebSocket connect/reconnect attempts made after close, as well as OkHttpWebSocketFactory.create(...) when
constructed with a closed-check, fail fast with IllegalStateException("root client has been closed").
4.22.0
(feat): Generate the shared WebhooksHelper from the API-wide api.settings.webhook-signature
setting in generators.yml (IR sdkConfig.webhookSignatureVerification). The helper is
emitted even when the API definition models no webhooks; webhook-specific signature
overrides still produce their own named helpers.
(fix): Fix the generated ReconnectingWebSocketListener connection-timeout message, which was missing the
closing parenthesis after the retry attempt number (e.g. ... milliseconds (retry attempt #2).
4.21.2
(fix): RetryInterceptor no longer discards an API response it has already received (e.g. a 429)
when a later retry attempt fails, such as when the call timeout expires mid-retry. The
previous response is buffered and returned instead of surfacing a generic IOException.
4.21.1
(chore): Bump bundled @fern-api/generator-cli to 0.10.1. Auto-versioned MAJOR/MINOR bumps no longer
produce a version-only changelog.md block when the AI analysis returns an empty changelog
entry; the PR description, version bump reason, or commit message body is used instead.
4.21.0
(feat): Generated root clients (sync and async) now implement AutoCloseable. close() shuts down
the underlying OkHttpClient’s dispatcher executor and evicts its connection pool, but only
when the SDK created that OkHttpClient itself — a client supplied via Builder.httpClient(...)
is left running, since the caller owns its lifecycle. This lets short-lived applications (e.g.
CLIs, or code that finishes after using WebSockets) release the client’s background threads
instead of relying on JVM shutdown.
4.20.0
(feat): Object types with xml encoding metadata (e.g. TwiML defined via OpenAPI xml objects) now
implement XmlSerializable with toXml() and a static fromXml(String) parser, backed by new
core XmlWriter/XmlReader utilities. Builders gain per-child-element methods
(e.g. Response.builder().say(...)) and undiscriminated child unions dispatch fromXml by
element name. Builder.fromXml(String) returns a populated builder, unknown child elements are
preserved as XmlElements (readable via getAdditionalChildren(), addable via addChild(...))
and round-tripped by toXml(), and very large objects that use a builder-based constructor also
get fromXml. Types without xml metadata are unchanged.
4.19.3
(fix): Generated ReconnectingWebSocketListener now acknowledges peer-initiated close frames by
overriding onClosing, so OkHttp completes the close handshake and onClosed (and the
public disconnect lifecycle) fires for server-initiated closes. A no-status close (local
sentinel 1005) is acknowledged with 1000, since 1005 is not a wire-valid close code.
The reconnect decision is exposed as a protected boolean shouldReconnectAfterClose(WebSocket, int, String)
hook (default unchanged: code != 1000) so resource-specific subclasses can treat a
protocol-established terminal close as final. Adds send(String, Consumer<WebSocket>) /
sendBinary(ByteString, Consumer<WebSocket>) overloads that report which socket directly
accepted a message (not invoked for queued or dropped messages).
4.19.2
(fix): Respect custom OAuth token headers and prefixes for client credentials authentication.
4.19.1
(fix): Honor an endpoint’s retries: {disabled: true} configuration (the x-fern-retries OpenAPI
extension). A call to such an endpoint tags its request with a max-retries override of zero, so it
bypasses automatic retries regardless of the client-wide ClientOptions.maxRetries or a
per-request RequestOptions.maxRetries. Endpoints without the configuration are unchanged.
4.19.0
(fix): With respect-optional-request-body enabled, a call that leaves out an optional request body now
passes a null body to OkHttp instead of an empty one, so the request carries no body at all. The
methods OkHttp requires a body for (POST, PUT, PATCH) still send an empty body, which is what
endpoints with no request body at all already send.
(feat): Add a respect-optional-request-body option. When enabled, an endpoint whose request body the API
does not require also gets an overload without the body parameter, and calling that overload sends
no body. The body parameter keeps its own type, so alpha(String id, Body body) gains a sibling
alpha(String id) rather than becoming alpha(String id, Optional<Body> body). Examples that
supply no body render as client.alpha(id). That call also leaves out the Content-Type header,
which now only goes out when a body does. The option defaults to false, so existing signatures
and snippets are unchanged.
4.18.2
(fix): Build an empty request body in snippets for an endpoint example that omits a body the wrapped
request requires, instead of leaving the builder’s required body stage unset.
4.18.1
(fix): Fix the generated OAuth token supplier when the get-token request has required properties
other than the client credentials. The builder calls are now ordered by the request’s
required properties, so the staged builder’s stages are satisfied in order, and hardcoded
values (such as grant_type) are written using the property’s type, which means an
enumerated grant_type gets the generated enum constant instead of a string literal.
4.18.0
(feat): Add support for the FERN_JAVA_SKIP_FORMATTING environment variable. When set to a truthy
value (1, true, yes, or on), the generator skips the ./gradlew :spotlessApply pass
that normally runs after code generation, so generation makes no Gradle invocation at all.
This unblocks restricted networks that cannot reach the Gradle distribution or plugin
repositories, and shortens generation time.
4.17.1
(fix): Fixed wire tests generated for APIs with auth: any combining Basic and OAuth schemes.
Tests no longer assert both a Bearer and a Basic Authorization header on the same
request; only the OAuth Bearer assertion is emitted since the client sends the OAuth
token when OAuth credentials are configured.
4.17.0
(feat): Add opt-in allowUserAgentAppInfo config. When enabled, the generated client’s builder exposes
an appInfo(name, version, comment) option whose product token is appended to the User-Agent
header the SDK would otherwise send ({sdk}/{version} ... {name}/{version} ({comment})),
following RFC 9110. Disabled by default, so existing generated output is unchanged, and it
composes with includePlatformHeaders, the runtime-version jar-manifest path, the configured
user-agent template value, and the default {coordinate}/{version}. Caller-supplied values are
trimmed and sanitized (name/version percent-encoded to RFC 7230 tchars; comment delimiters,
control characters and any non-ASCII characters percent-encoded as UTF-8 bytes so OkHttp’s header
validation never rejects them), so untrusted values cannot inject header content. Still overridable
by an explicit User-Agent and suppressed by omit-fern-headers.
4.16.0
(feat): Add an opt-in runtime-version config flag. When enabled, the SDK version reported
in the telemetry headers (X-Fern-SDK-Version and the version segment of
User-Agent) is resolved at runtime from the jar manifest’s Implementation-Version
attribute instead of being baked in as a literal at generation time. The generated
build.gradle records the project version in the jar manifest, so an external tool such
as release-please only has to rewrite a single version line for the reported version
to track the actually-published artifact — rather than a version the SDK may never
publish. Falls back to the generation-time version when the manifest attribute is
absent (e.g. running from unpackaged classes). Disabled by default, so existing
generated output is unchanged, and subject to omitFernHeaders.
4.15.5
(fix): Resolve the X-Fern-SDK-Name header from the configured Maven coordinate in
GitHub output mode. Previously the header was read from the (Fiddle-synthesized)
publish config, so it emitted com.<org>.fern:<workspace>-sdk instead of the
coordinate the user configured under output.coordinate, leaving it inconsistent
with the User-Agent product token. The header now reads
output.mode.github.publishInfo.maven.coordinate, which is populated for both
remote and --local generation. Also emits X-Fern-SDK-Version in cases where a
coordinate resolves without a version previously being set.
4.15.4
(fix): Global headers now honor their env and client-default values. The
builder initializes the header from the environment variable when set,
otherwise the client default, and an explicitly provided value still takes
precedence. Previously a global header was only sent when the caller set it
explicitly.