Repository navigation
2.0 findings: release() migration note, JavaDoc coverage and device build() throws #1255
Description
These came up while validating the Pub/Sub 2.0 website docs against integration/v2 at f7f4363cd3caa0bed523c7751ebdcfbcb3c33221. They're grouped here so they can be triaged together. If you'd rather track any of them on its own, split it into a separate issue.
Summary
| # | Finding | Priority | Website docs depend on it? | Recommendation |
|---|---|---|---|---|
| 1 | UPGRADING.md misses the release() change (now a checked exception and 90011) and two error-code fixes. The README, CONTRIBUTING and the 40019 LiveObjects error still name 1.x artifacts |
P1 | Yes, in part: the 2.0 LiveObjects docs quote the 40019 message | Add a release() section to UPGRADING, finish moving the README and CONTRIBUTING to 2.0 coordinates, and fix the 40019 message |
| 2 | The published JavaDoc covers only core. PubSubServer and PubSubDevice are missing, and the device javadoc jar is empty |
P2 | Yes: the ably.com Java API reference will need to read this path for 2.0 | Publish one aggregated reference: Javadoc for core and server, Dokka for device |
| 3 | PubSubDevice.ClientBuilder.build() throws a checked AblyException but doesn't declare it, so Java callers can't catch it by type |
P2 | No | Add @Throws(AblyException::class) |
P1 = should be fixed before the 2.0 release · P2 = should be fixed soon · P3 = nice to have.
1. UPGRADING.md misses the release() and error-code changes; README, CONTRIBUTING and the 40019 error still name 1.x (P1)
What's wrong
The 2.0 code is in place, but the upgrade guide and several user-facing strings still describe 1.x:
- UPGRADING.md doesn't say that
Channels.release()now throws, and it doesn't mention two error-code fixes. - README.md is half-migrated: its install lines are for 1.x, but its sample is for 2.0.
- CONTRIBUTING.md and the LiveObjects-missing error (40019) still name 1.x artifacts.
Evidence
release() is a source break that UPGRADING.md doesn't mention
-
On a realtime client,
Channels.release()now declaresthrows AblyException. It throws90011(status 400) unless the channel is INITIALIZED, DETACHED or FAILED (PubSubRealtimeClient.java#L181,#L229-L233). In 1.x,release()detached the channel implicitly. -
AblyExceptionis a checked exception, so every existing Java call torelease()fails to compile. Compilingc.channels.release("x")against the jars built atf7f4363cgives:Rel.java:1: error: unreported exception AblyException; must be caught or declared to be thrown -
The
release()Javadoc (#L171-L180) doesn't name 90011. The Kotlin adapter's KDoc does (Channels.kt#L47).
Two error-code fixes may reach users first in 2.0
Both were merged to main after 1.8.2 and are on integration/v2. If no further 1.x release ships them first, 2.0.0 is where users first see them, and the guide should say so:
- A closing or closed connection reports
80017/ 400 instead of10000/ 200 (fix: report error code 80017 for a closed connection #1252). - Client-side errors that had
codeandstatusCodeswapped now report, for example,code = 40000, statusCode = 400(fix: pass ErrorInfo status code before Ably code in client-side errors #1248). Code that matched oncode == 400will behave differently.
README.md is half-migrated
The note at #L29-L31 says the install instructions still describe 1.x. The sample right below them already uses PubSubServer.realtimeClientBuilder() (#L84-L91), which doesn't exist in the artifact those install lines add. So code that follows the README as written doesn't compile.
The release-process version bump won't fix these lines, because they need new coordinates, not a new version number:
#L2: the version badge resolvesio.ably/ably-java.#L25:io.ably.pubsub:deviceis labelled "(aar)". It is a Kotlin Multiplatform artifact (device,device-jvm,device-android), and Maven users must depend ondevice-jvm(UPGRADING.md#L37-L39).#L54says "Android >=4.4 (API level 19)", butdevicehasminSdk = 24. UPGRADING already says so at#L41-L43.#L63-L80: the Maven and Gradle install lines areio.ably:ably-java:1.8.2.#L139-L142: LiveObjects is installed asio.ably:liveobjects:1.8.2.
CONTRIBUTING.md
#L38-L48says the library ships with theHttpURLConnection/Java-WebSocket engine by default, and shows how to switch to OkHttp usingio.ably:ably-java. The build has defaulted to OkHttp for a while (core/build.gradle.kts#L27-L31); this text was already stale in 1.x. The coordinates are also 1.x.#L238:ably-android-1.8.2.aar.
LiveObjects-missing error (40019)
RealtimeObject.java#L89 tells users to add runtimeOnly('io.ably:liveobjects:<ably-version>'). In 2.0 the plugin is io.ably.pubsub:liveobjects (UPGRADING.md#L45-L47).
A user who follows the message gets the 1.x plugin, whose implementation class is io.ably.lib.liveobjects.DefaultLiveObjectsPlugin. 2.0 looks for io.ably.pubsub.liveobjects.DefaultLiveObjectsPlugin (LiveObjectsPlugin.java#L89), so it still doesn't find the plugin, and the same error comes back.
History
- The
release()change is deliberate. It came in with 2943501 (Simon Woolf, "Make releasing a realtime channel that isn't detached an error (RTS4c-e)") via Make releasing a realtime channel that isn't detached an error (RTS4c-e) #1251. That PR has a "Breaking change" and a "Migration" paragraph: "callchannel.detach(listener)and wait for it to complete before callingchannels.release(name)". The note just never reached UPGRADING.md. This item asks for it to be documented, not changed. - The 40019 string is a leftover. It was carried unchanged through bfb6de1 ([AIT-996] feat(liveobjects): add path-based RealtimeObject and channel.object accessor #1216) and the later group and package moves.
- The README note was a deliberate placeholder. 4ead47f ([Split 3/3] Lockstep release pre-flight, dry-run CI job, README note #1234) added it, saying the 1.x installation instructions still apply. The sample was migrated later; the install lines weren't.
- The 2.0 release PR review raised the missing migration guide. The thread (Integration branch for io.ably.pubsub:* v2 packages #1245 (comment)) was resolved. UPGRADING.md was added afterwards, but without these items. A second thread about the undocumented minSdk 24 (Integration branch for io.ably.pubsub:* v2 packages #1245 (comment)) is still open: UPGRADING now covers it, but the README doesn't.
- Some of this is already a release step. The CHANGELOG entry and the README version numbers are steps in the CONTRIBUTING "Release Process" (steps 2 and 3), and the open feat: add release automation command
/releasewith version bump and CHANGELOG update logic #1204 automates them. The new coordinates, the minSdk line, the "(aar)" label, the UPGRADING content and the 40019 string are not covered by a version bump. - Why the error-code rows are conditional.
mainalso carries PR 1250 ("Deprecate releasing a realtime channel that isn't detached (RTS4b)"), which only makes sense in a 1.x release. So another 1.x release looks planned, and it might ship those two fixes first.
Impact
- Java users upgrading to 2.0 hit a compile error on
release(). Nothing in the upgrade guide explains it or the new requirement to detach first. - The README is the first thing users read after the release, and its install lines contradict its sample.
- The 2.0 LiveObjects docs quote the 40019 message, and following the message as written doesn't fix the error.
Recommendation
- UPGRADING.md: add a
release()section covering the checked exception, 90011, and callingdetach()and waiting beforerelease(). Add rows for 80017 and the code/statusCode fix, unless a 1.x release ships them first. - Javadoc: name 90011 in the
release()Javadoc. - CHANGELOG: when the 2.0.0 entry is generated, call out the
release()change as breaking. - README: update the badge, the artifact table, the Android requirement, the install lines (Gradle
device/server, Mavendevice-jvm/server) and the LiveObjects install line. Then drop the "still describe 1.x" note. - CONTRIBUTING: update the engine section and the AAR line.
- 40019 message: change it to
io.ably.pubsub:liveobjects:<version>.
2. The published JavaDoc covers only core: PubSubServer and PubSubDevice are missing (P2)
What's wrong
The JavaDoc workflow uploads only the core module's output. So the published 2.0 reference doesn't include the classes users create clients with.
Evidence
.github/workflows/javadoc.yml#L36-L44runs./gradlew javadoc, but uploads onlycore/build/docs/javadoc.- The
corejavadoc coverssrc/main/javaand../lib/src/main/java(core/build.gradle.kts#L45-L50). Neither containsio.ably.pubsub.serverorio.ably.pubsub.device. ./gradlew javadocdoes buildserver/build/docs/javadoc, withPubSubServerand its builders. That directory is never uploaded.deviceis Kotlin Multiplatform, so it has nojavadoctask, and the build has no Dokka. As a result:PubSubDevicehas no generated reference anywhere.- Its Maven javadoc jars are configured as empty (
device/build.gradle.kts#L95,JavadocJar.Empty()). The builtdevice-jvm-javadoc.jarcontains only a manifest.
The preview that the workflow uploaded for the release PR shows the gap. https://sdk.ably.com/builds/ably/ably-pubsub-java/pull/1245/javadoc/ lists the http, realtime, types and liveobjects packages (among others), but has no io.ably.pubsub.server or io.ably.pubsub.device package. …/io/ably/pubsub/server/PubSubServer.html is not found.
Gradle output and preview status codes
In a worktree at f7f4363c:
$ ./gradlew javadoc --dry-run | grep ':javadoc'
:core:javadoc :gradle-lint:javadoc :liveobjects:javadoc :network-client-core:javadoc
:network-client-default:javadoc :network-client-okhttp:javadoc :pubsub-adapter:javadoc :server:javadoc :uts:javadoc
$ ./gradlew :server:javadoc :core:javadoc
$ ls server/build/docs/javadoc/io/ably/pubsub/server/
PubSubServer.html PubSubServer.ClientBuilder.html PubSubServer.HttpClientBuilder.html PubSubServer.RealtimeClientBuilder.html …
$ ls core/build/docs/javadoc/io/ably/pubsub/server
ls: …: No such file or directory
The workflow uploaded this preview from the release PR head ba473268 on 2026-10-15. javadoc.yml hasn't changed since then.
https://sdk.ably.com/builds/ably/ably-pubsub-java/pull/1245/javadoc/index.html 200
…/io/ably/pubsub/realtime/PubSubRealtimeClient.html 200
…/io/ably/pubsub/server/PubSubServer.html 403
…/io/ably/pubsub/device/PubSubDevice.html 403
allclasses-index.html packages: annotation debug http liveobjects platform push realtime transport types util (no server, no device)
History
- The upload path predates the door modules. It comes from a mechanical rename in 518819d ([Split 1/3] Restructure modules: java/android → core/core-android under io.ably.pubsub #1232: "
javamodule becomescore"), made beforeserveranddeviceexisted. So this looks accidental. - The empty device javadoc jar has no recorded reason.
JavadocJar.Empty()came in with 686bdfd ("Add the device and server door packages with side-declaring agents"). The commit message doesn't mention docs. - The release PR review asked for this. The request to generate and publish docs for
deviceandserver(Integration branch for io.ably.pubsub:* v2 packages #1245 (comment)) was resolved without a reply or a change.
Impact
- The README tells users not to depend on
coredirectly. In 2.0, the supported way to create a client isPubSubServer.httpClientBuilder()/realtimeClientBuilder()orPubSubDevice.clientBuilder(), and the published reference shows none of them. - The workflow also runs on push to
main. Todaysdk.ably.com/builds/ably/ably-pubsub-java/main/javadoc/serves the 1.x reference; once 2.0 merges, it becomes this core-only reference. That's the path the ably.com Java API reference will need to read for 2.0. (The ably.com reference still reads the pre-renameably-java/path today.) - IDEs show no documentation for
io.ably.pubsub:devicefrom Maven Central, because its javadoc jars are empty.
Recommendation
Either:
- generate one aggregated reference covering
coreandserver(Javadoc) anddevice(Dokka, which can also produce real javadoc jars fordevice), and upload that; or - upload each module's docs under its own path, and link them from the core overview page.
3. PubSubDevice.ClientBuilder.build() throws a checked AblyException but doesn't declare it (P2)
What's wrong
PubSubDevice.ClientBuilder.build() (PubSubDevice.kt#L411-L419) documents @throws io.ably.pubsub.types.AblyException in its KDoc, and it does throw it: it calls RealtimeClientFactory.create(...), which throws AblyException.
It isn't annotated with @Throws(AblyException::class), though. So the compiled method has no throws clause, and Java code can't catch the exception by type.
Evidence
try {
PubSubRealtimeClient client = PubSubDevice.clientBuilder().key(key).build();
} catch (AblyException e) { // error: exception AblyException is never thrown in body of corresponding try statement
}The exception is real. For example, PubSubDevice.clientBuilder().autoConnect(false).build() with no auth options throws io.ably.pubsub.types.AblyException at runtime (code 40000, "No authentication parameters supplied").
The server door declares the same exception, so the same Java code compiles against io.ably.pubsub:server:
HttpClientBuilder.build() throws AblyException(PubSubServer.java#L532)RealtimeClientBuilder.build() throws AblyException(#L656)
Compile and runtime output
Against the jars built at f7f4363c:
$ javac … src/DeviceBuild.java # try { PubSubDevice.clientBuilder().key("a:b").build(); } catch (AblyException e) { }
DeviceBuild.java:5: error: exception AblyException is never thrown in body of corresponding try statement
$ javac … ctl/ServerBuild.java # the same pattern on PubSubServer.realtimeClientBuilder()...build()
rc=0
At runtime, with no network (autoConnect(false), no auth):
caught io.ably.pubsub.types.AblyException checked=true code=40000 msg=java.lang.Exception: No authentication parameters supplied
The bytecode agrees: PubSubDevice$ClientBuilder.build() has no Exceptions: attribute, while both server build() methods declare throws io.ably.pubsub.types.AblyException.
History
- This looks like an omission.
build()came in with 686bdfd with the KDoc@throwsbut no@Throws. Later commits changed its KDoc and body without adding the annotation. The server door declares the exception. - The internal Kotlin adapter is out of scope on purpose. Its
Channels.release()has the same shape, but leaving@Throwsoff there was a deliberate choice (Simon Woolf's reply at Make releasing a realtime channel that isn't detached an error (RTS4c-e) #1251 (comment)): that interface is shared by the REST and realtime clients, and none of the adapter's other throwing methods declare it. This issue covers only the public device entry point.
Impact
- Java apps on
io.ably.pubsub:device(Android apps written in Java, JVM desktop clients) can't handle a client-construction failure asAblyException. They have to catchExceptionand cast. That's unlike the server door, and unlike what the KDoc says. PubSubDevice.clientBuilder()is the only supported way to create a client on the device side, so every Java device app goes through this method.
Recommendation
- Add
@Throws(AblyException::class)toPubSubDevice.ClientBuilder.build(). It's available in common code. - Optionally, add a Java source compile test in the device module that catches
AblyExceptionaroundbuild(), so that a missing annotation fails the build.
Checklist
- UPGRADING.md:
release()section (checked exception, 90011, detach first), plus the 80017 and code/statusCode rows unless they ship in 1.x first. README, CONTRIBUTING and the 40019 message moved to 2.0 coordinates - Published JavaDoc includes
serveranddevice(and realdevicejavadoc jars) -
@Throws(AblyException::class)onPubSubDevice.ClientBuilder.build()
Related
- 2.0 release PR: Integration branch for io.ably.pubsub:* v2 packages #1245, including the review threads linked above
- Make releasing a realtime channel that isn't detached an error (RTS4c-e) #1251: the
release()change and its migration note - fix: report error code 80017 for a closed connection #1252 and fix: pass ErrorInfo status code before Ably code in client-side errors #1248: the 80017 and code/statusCode fixes; Deprecate releasing a realtime channel that isn't detached (RTS4b) #1250: the 1.x RTS4b deprecation on
main - feat: add release automation command
/releasewith version bump and CHANGELOG update logic #1204: release-process automation (CHANGELOG and version bump) - Presence.get(true) blocks execution for some time #694:
Presence.get()called from a presence listener stalls the connection. This is a separate item; I'm adding 2.0 details there as a comment.
Activity