Visitar URL original
fix(mau): surface identity and connection failures instead of hiding them by ttypic · Pull Request #462 · ably/ably-cli · GitHub
Skip to content

fix(mau): surface identity and connection failures instead of hiding them - #462

Open
ttypic wants to merge 1 commit into
integration/mau-fix-token-issuancefrom
integration/mau-connection-failures
Open

ttypic wants to merge 1 commit into
integration/mau-fix-token-issuancefrom
integration/mau-connection-failures

Conversation

@ttypic

@ttypic ttypic commented Oct 1, 2026 •

Copy link
Copy Markdown

Add a hint for 40012 (invalid client ID), and make the client ID hints
(40012, 40161, 91000) context-aware: under token auth they point at
re-issuing the token, and they only suggest --client-id on commands
that have it, falling back to ABLY_CLIENT_ID.

  • Spaces connection and cursor-attach failures keep the Ably error code
    and status instead of wrapping them in a plain Error.
  • Long-running commands fail with a non-zero exit when their connection
    dies mid-run: on failed, or when it drops five times in a row without
    staying connected for 10s, which is how a server-side rejection such
    as an eviction can present. Previously a subscribe that lost its
    connection logged a warning and exited 0.
  • The --duration exit path force-exits before finally() runs, so it now
    emits the standard completed JSON status itself instead of an ad-hoc
    complete line, and prints its human message to stderr.

@vercel

vercel Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cli-web-cli Ready Ready Preview Oct 5, 2026 6:57pm UTC

Request Review

@claude-code-ably-assistant

Copy link
Copy Markdown

Walkthrough

This PR fixes a class of silent failures where identity errors (e.g. invalid clientId, token auth scope issues) and connection failures were swallowed and the CLI appeared to hang rather than exiting with a meaningful error. It introduces a ConnectionHealthMonitor that detects dead connections and aborts long-running commands, and makes error hints context-aware so they adapt to whether the user is using token auth or the --client-id flag.

Changes

Area Files Summary
Core src/base-command.ts Integrates ConnectionHealthMonitor; adds connectionLost abort controller; refactors finally() into logCompletedStatus(); threads hint context (token auth, --client-id presence) into fail()
Utils src/utils/connection-health.ts (new) ConnectionHealthMonitor class — detects failed/reconnection-loop states and signals abort
Utils src/utils/errors.ts Adds errorWithReason() to preserve Ably error metadata; adds HintContext interface; makes error hints functions that adapt per context
Utils src/utils/long-running.ts Extends ExitReason with "aborted"; waitUntilInterruptedOrTimeout() now accepts an optional abort signal
Commands src/spaces-base-command.ts Suspended/failed connection handlers now call errorWithReason() to surface Ably error details
Tests test/unit/commands/channels/subscribe.test.ts Adds case: subscribe fails when the connection dies mid-stream
Tests test/unit/utils/connection-health.test.ts (new) Full coverage of ConnectionHealthMonitor (failed state, reconnection loops, clean disconnects)
Tests test/unit/utils/errors.test.ts Tests errorWithReason() and context-aware hint dispatch
Tests test/unit/utils/long-running.test.ts Tests abort signal path in waitUntilInterruptedOrTimeout()
Docs/Config AGENTS.md, docs/Project-Structure.md, .claude/skills/ably-new-command/SKILL.md Updated to document new utility and context-aware hint pattern

Review Notes

  • Behavioral change — commands that previously hung on connection failure (identity errors, token scope issues) will now exit with an error. Any existing scripts that relied on the hang behaviour (e.g. a watchdog timeout) will see a different exit path.
  • HintContext is a new public interface in errors.ts — if other modules define hints in future, they'll need to match this shape. Worth confirming the interface is stable before more hints adopt it.
  • connectionLost AbortController lives on the base command instance — ensure subclasses that override run() without calling waitAndTrackCleanup() still get the abort behaviour, or document the contract.
  • Reconnection-loop detection threshold (the heuristic inside ConnectionHealthMonitor) should be reviewed for tuning — aggressive thresholds could cause false-positive aborts on flaky networks.
  • New files (connection-health.ts, connection-health.test.ts) are well-tested; the subscribe integration test covers the end-to-end path.

@ttypic
ttypic requested a review from umair-ably October 1, 2026 13:01

@claude-code-ably-assistant claude-code-ably-assistant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: PR 462 — fix(mau): surface identity and connection failures instead of hiding them

Overall: LGTM with one minor note. The architecture is solid and the tests are thorough.

Summary

File Status Notes
src/utils/connection-health.ts ✓ Clean, well-tested new utility
src/utils/long-running.ts ✓ Abort signal integration is correct
src/utils/errors.ts ✓ Context-aware hints are a nice improvement
src/base-command.ts ✓ The abort/fail wiring is correct
src/spaces-base-command.ts ✓ Error code preservation fixes a real gap
test/unit/utils/connection-health.test.ts ✓ Good coverage of the boundary conditions
test/unit/utils/errors.test.ts ✓
test/unit/utils/long-running.test.ts ✓ Covers already-aborted signal — important edge case
test/unit/commands/channels/subscribe.test.ts ✓

Findings

src/base-command.ts — waitAndTrackCleanup timeout path (minor)

The old status: "complete" (no trailing 'd') that was emitted by the --duration timeout path is now replaced with status: "completed" + exitCode. This aligns it with the finally() path and fixes an inconsistency. Worth calling out explicitly for anyone with external JSON consumers watching that specific key, though no tests were checking the old shape so it wasn't load-bearing.

src/utils/connection-health.ts — stable-connection reset logic (correct, noting it for visibility)

When a disconnected/suspended event follows a stable connection (≥10s), unstableDrops resets to 1, not 0. That's intentional — the current drop counts as the first of a new run — but it means a connection that repeatedly stays up for exactly 10s and then drops needs 5 additional drops after each stable period before the monitor fires. The comment in STABLE_CONNECTION_MS explains the intent. The test "restarts the count after a stable connection" validates this correctly.

src/base-command.ts:2168 — tokenAuth detection scope

tokenAuth: Boolean(process.env.ABLY_TOKEN),

This is consistent with every other ABLY_TOKEN check in the file (e.g., lines 508, 802, 1234) and correctly targets the main token auth use case. ably login stores API keys, not tokens, so the env-var check covers the primary scenario. No issue here.

waitAndTrackCleanup — already-aborted signal edge case (correct)

If connectionLost fires between setupConnectionStateLogging() and waitAndTrackCleanup() being called, waitUntilInterruptedOrTimeout correctly handles the pre-aborted signal:

if (signal?.aborted) {
  handleExit("aborted");
} else {
  signal?.addEventListener("abort", abortHandler);
}

No race possible in single-threaded JS. ✓


No issues blocking merge

The PR does exactly what it says: a connection failed event or 5 consecutive drops without staying connected now produces a non-zero exit instead of a silent success. The JSON contract change (status: "complete" → status: "completed" with exitCode) is a fix, not a regression. Tests cover all the important boundary conditions.

@umair-ably
umair-ably added this pull request to stack #465 October 1, 2026 14:14
@sacOO7
sacOO7 requested a lite review from Copilot October 5, 2026 09:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Moderate review findings remain around duration completion coverage and preserving connection error metadata.

Review effort: Lite
Findings: None

What changed in this PR

Improves MAU identity hints and ensures connection failures retain Ably details and produce non-zero exits.

Changes:

  • Adds context-aware client ID hints and structured error propagation.
  • Detects failed or unstable realtime connections.
  • Standardizes duration completion JSON output.
  • Expands lifecycle, error, and connection tests and documentation.
File Description
test/​unit/​utils/​long-running.test.ts Tests abort behavior.
test/​unit/​utils/​errors.test.ts Tests structured errors and hints.
test/​unit/​utils/​connection-health.test.ts Tests connection health behavior.
test/​unit/​commands/​channels/​subscribe.test.ts Tests non-zero exits on connection failure.
src/​utils/​long-running.ts Supports abortable waits.
src/​utils/​errors.ts Adds metadata-preserving errors and contextual hints.
src/​utils/​connection-health.ts Detects failed and unstable connections.
src/​spaces-base-command.ts Preserves connection and cursor error metadata.
src/​base-command.ts Integrates connection monitoring and completion handling.
docs/​Project-Structure.md Documents new utilities.
AGENTS.md Updates error-hint guidance.
.claude/​skills/​ably-new-command/​SKILL.md Updates lifecycle guidance.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

…them

- Add a hint for 40012 (invalid client ID), and make the client ID hints
  (40012, 40161, 91000) context-aware: under token auth they point at
  re-issuing the token, and they only suggest --client-id on commands
  that have it, falling back to ABLY_CLIENT_ID.
- Spaces connection and cursor-attach failures keep the Ably error code
  and status instead of wrapping them in a plain Error.
- Long-running commands fail with a non-zero exit when their connection
  dies mid-run: on `failed`, or when it drops five times in a row without
  staying connected for 10s, which is how a server-side rejection such
  as an eviction can present. Previously a subscribe that lost its
  connection logged a warning and exited 0.
- The --duration exit path force-exits before finally() runs, so it now
  emits the standard `completed` JSON status itself instead of an ad-hoc
  `complete` line, and prints its human message to stderr.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 5329084b Deployed Oct 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants