Skip to content

2.0 findings: release() never fails with 90011, ably-flutter agent, upgrade polish #611

Open

Description

@sacOO7

These came up while validating the Pub/Sub 2.0 website docs against integration/v2 at 1202d99bc7f8e561ba4e0a6c1ddafbc562740426. 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 RealtimeChannels.release() is documented to fail with 90011, but the pinned 1.x native SDKs never produce it. The native bump also forces an endpoint decision P1 Yes: until it lands, the Flutter docs keep the 1.x "detach before release" wording Bump to the native next majors, forward the release error on both platforms, and replace environment/restHost/realtimeHost with endpoint
2 Ably-Agent still identifies as ably-flutter and has no ably-pubsub-device flag P1 No Once ably/ably-common#361 merges, rename the agent to ably-pubsub-flutter and add the device flag
3 UPDATING.md doesn't say to remove ably_flutter, and the two packages can't be used in one app P2 Yes: the website push guide relies on this note Add one line to UPDATING.md
4 Connection.ping() always throws UnimplementedError (since 1.x) P3 No Implement it, or remove it while 2.0 allows breaking changes
5 createClient() failures surface as several unhandled async errors (since 1.x) P3 No Validate obvious option errors in Dart, or report creation failures once

P1 = should be fixed before the 2.0 release · P2 = should be fixed soon · P3 = nice to have.

1. RealtimeChannels.release() documents error 90011, but the pinned 1.x native SDKs never produce it (P1)

What's wrong

On integration/v2, release() is documented to fail with 90011 for a channel that isn't INITIALIZED, DETACHED or FAILED (RTS4e). On a device, though, releasing an attached channel still succeeds and detaches it implicitly, which is the 1.x behaviour.

This is known (see History). This item tracks the work to the 2.0 release, together with the ClientOptions change that the native bump forces.

Evidence

What the docs say

  • realtime_channels.dart#L20-L28: "If the channel is in any other state, the returned future completes with an [AblyException] with code 90011 and the channel is not released."
  • UPDATING.md#L24: "Releasing a channel in any other state no longer detaches it implicitly. Instead, the future completes with an AblyException with code 90011…"

What happens

I didn't reproduce this on a device. It follows from the native sources quoted above: neither native release has an error path that could reach the handler, and the handler has no error path either.

The native bump has a knock-on change

History

Impact

  • 2.0 documents a breaking change (RTS4e) that doesn't happen on devices. Apps written against the docs never see 90011, for example ones that catch it to decide whether to detach first.
  • Until this lands, the ably.com docs have to keep the 1.x "detached before release" wording for Flutter, along with the await channel.detach(); await realtime.channels.release(name); pattern.

Recommendation

  1. Bump to the native next-major releases that implement RTS4c-e.
  2. Forward the release error on both platforms:
    • Android: in releaseRealtimeChannel, catch the AblyException that release now throws and pass it to handleAblyException(result, e).
    • iOS: call release:error:, and when it returns NO, return a FlutterError built from the ARTErrorInfo.
  3. Add a device or integration test: releasing an attached channel completes with 90011 (statusCode 400), and the channel is still there.
  4. Replace environment/restHost/realtimeHost with endpoint (or map them), and document it in UPDATING.md.
  5. Optionally, add a 90011 constant to ErrorCodes (error_codes.dart#L2-L9).
  6. If 2.0 has to ship before the native majors, change the dartdoc and UPDATING.md#L24 to describe what the plugin actually does.

2. Ably-Agent still identifies as ably-flutter and has no ably-pubsub-device flag (P1)

What's wrong

#604 deliberately left the ably-flutter/<version> agent identifier unchanged, noting that "the family rename belongs to the split, gated on ably-common#361". The split is now on integration/v2, but the plugin still builds its native clients with the 1.x agents. So 2.0 traffic can't be told apart from ably_flutter 1.x traffic by identifier.

Evidence

What it sends now (derived from the source, not captured on a device; pubspec version 1.2.44):

Android: ably-java/1.6.0 dart/<dart-version> ably-flutter/1.2.44 android/<api-level>
iOS:     ably-cocoa/1.2.53 ably-flutter/1.2.44 dart/<dart-version> iOS/<os-version>

The same value goes in the HTTP Ably-Agent header and the realtime agent query param.

History

Impact

  • The 2.0 agent convention in Register the Pub/Sub split agent identifiers: per-side flags and per-language families ably-common#361 (open, approved) would register ably-pubsub-flutter as Flutter's 2.0 identifier, a wrapper over the Java and Cocoa cores.
    • Each SDK flips to its new identifier at the new major: "old identifier ⇒ legacy-package traffic, new ⇒ new-package traffic".
    • Every 2.0 client also sends the versionless side flag, ably-pubsub-device or ably-pubsub-server.
  • If 2.0.0 ships with ably-flutter and no side flag, its traffic counts as legacy-package traffic. Migration tracking and 1.x end-of-life work can then separate it only by version number. That can't be fixed after release for the versions already out.

Recommendation

  • Once Register the Pub/Sub split agent identifiers: per-side flags and per-language families ably-common#361 is merged: rename the plugin's agent entry from ably-flutter to ably-pubsub-flutter, and add the versionless ably-pubsub-device flag on both platforms:
    • Android: put("ably-pubsub-device", null). ably-java writes a null version as the bare name.
    • iOS: set the value to the exported ARTClientInformationAgentNotVersioned constant. ably-cocoa compares it by pointer, so a string literal won't work.
  • If the natives are bumped for 2.0 (item 1 above): create the clients through their device entry points instead (PubSubDevice builder, ARTPubSubDevice). These add the flag themselves. Keep the plugin's own ably-pubsub-flutter entry.
  • Tests: add a unit test on both platforms that asserts the agents map.

3. UPDATING.md doesn't say to remove ably_flutter, and the two packages can't be used together (P2)

What's wrong

UPDATING.md#L7-L10 says the package was renamed, but not that the old one has to go. The new package keeps the old native identity, so the two clash in one app.

Evidence

  • The new package keeps the old native identity:
    • the same Android package and plugin class (pubspec.yaml#L22-L29, io.ably.flutter.plugin / AblyFlutter);
    • the same Java and ObjC class names;
    • the same method-channel names (io.ably.flutter.plugin, io.ably.flutter.stream).
  • flutter pub get accepts both packages without complaint (checked). The clash only shows up later:
    • duplicate-class errors in the Android build;
    • duplicate or ambiguous ObjC classes on iOS;
    • two plugins on one method channel.
  • Caveat: I couldn't run the two-plugin Android or iOS build for this check. The build failures are inferred from the identical class and channel names above, plus the same duplicate-class failure mode in Version conflict with Intercom Android SDK #584.

History

This is an omission in the 2.0 package rename (ea51456f). Nothing records a decision to leave it out.

Impact

An app that migrates incrementally, or picks up ably_flutter through another dependency, hits the clash with no warning from the upgrade guide.

Recommendation

Add to UPDATING.md: "Remove ably_flutter from your dependencies; the two packages cannot be used in the same app."

4. Connection.ping() always throws (P3)

What's wrong

connection.dart#L107-L115 documents a round-trip ping, but the body is throw UnimplementedError();, thrown synchronously.

Evidence

The same Dart-level repro as item 5 shows ping(): UnimplementedError. The body is identical on main and in every tag since 1.2.0.

History

This is long-standing 1.x behaviour, not a 2.0 regression. It has been unimplemented since b8104ca8 (2020).

Impact

Code that follows the dartdoc fails at runtime. Both native SDKs support ping.

Recommendation

Either implement it, or remove it while 2.0 still allows breaking changes.

5. createClient() failures surface as unhandled async errors (P3)

What's wrong

  • createClient() returns immediately (pub_sub_client.dart#L102-L103), and the native client is created in the background.
  • The resulting future has no error handler (platform_object.dart#L18-L22).
  • Connection, Push, and each channel's presence and push objects all chain off that future. So one creation failure produces several unhandled async errors, then a rethrow from every later call.

Evidence

Example: a relative authUrl such as '/auth'. On Android, ably-java rejects it in the Auth constructor, and the plugin returns that error from createRealtime (AblyMethodCallHandler.java#L308-L311). I didn't check iOS behaviour for a relative authUrl.

Dart-level reproduction (mocked platform channel)

Run with flutter test (Flutter 3.19.2) in a worktree at 1202d99b. The mock makes createRealtime throw the kind of PlatformException the Android handler returns for authUrl: '/auth'. The error code in the mock is illustrative; I didn't check which code ably-java assigns to a malformed URL.

createClient returned synchronously: PubSubClient
uncaught async errors: 2
  - AblyException: … message=(no protocol: /auth) …
  - AblyException: … message=(no protocol: /auth) …
connect(): rethrows AblyException: … no protocol: /auth …
attach(): rethrows AblyException: … no protocol: /auth …
ping(): UnimplementedError
00:01 +0 -1: … [E]   (further uncaught AblyExceptions from RealtimePresence / PushChannel created by channels.get())

History

This is long-standing 1.x behaviour, not a 2.0 regression. The same PlatformObject pattern drove Realtime(...) in 1.x (2020-2021); createClient() only wraps the constructor. #551 shows a user hitting a creation failure in 1.x as an unhandled exception.

Impact

Apps get unhandled async errors (one per dependent object) instead of a single, catchable failure. 2.0 is the cheap window to change this.

Recommendation

Either validate obvious option errors in Dart, or report creation failures once, through connection state or a documented error.

Checklist

  • Native bump to the RTS4c-e majors, release error forwarded on Android and iOS, endpoint replaces environment/restHost/realtimeHost (or the dartdoc and UPDATING are changed to match 1.x behaviour)
  • Agent renamed to ably-pubsub-flutter with the ably-pubsub-device flag, after Register the Pub/Sub split agent identifiers: per-side flags and per-language families ably-common#361 merges
  • UPDATING.md says to remove ably_flutter
  • Connection.ping() implemented or removed
  • createClient() failures reported once, or validated in Dart

Related

┆Issue is synchronized with this Jira Task by Unito

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions