Skip to content

fix(js/net)!: separate session and stream errors - #2794

Open
kixelated wants to merge 1 commit into
devfrom
claude/js-remote-error-registry
Open

fix(js/net)!: separate session and stream errors#2794
kixelated wants to merge 1 commit into
devfrom
claude/js-remote-error-registry

Conversation

@kixelated

@kixelated kixelated commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

SessionCode.Unauthorized and StreamCode.DeliveryTimeout are both 0x2 in disjoint registries. The previous RemoteError carried an untyped number, so reconnect logic could interpret a SETUP stream reset as a terminal authorization rejection. Reset forwarding could also send a session code back as a stream code.

Replace the shared error with registry-specific types:

  • SessionError.code: SessionCode
  • StreamError.code: StreamCode

SessionCode and StreamCode are branded numeric types. The transport decode boundaries apply the correct brand, including to unknown or application-defined codes, while TypeScript rejects mixing the registries.

The SETUP negotiation now races session.closed. An unauthorized close during the handshake becomes a SessionError and stops reconnecting, rather than being hidden behind a failed or stalled setup stream. Stream reset code 2 remains a retryable StreamError.

Reset forwarding now preserves only StreamError codes. Session and local errors take the plain-error path instead of being mistranslated onto a stream.

Public API changes

Breaking, targeting dev:

  • Remove RemoteError and RemoteSource.
  • Add SessionError and StreamError.
  • Brand SessionCode and StreamCode so they cannot be mixed or replaced with raw numbers.

@moq/net remains on the existing unreleased 0.3.0 line, so no additional version bump is needed.

Test plan

  • Compile-time tests reject session/stream code mixing and raw numeric construction.
  • Error boundary tests verify each transport registry produces its matching error class.
  • Reload tests cover unauthorized session closure both after establishment and during SETUP.
  • Reload tests verify a SETUP stream reset with code 2 still reconnects.
  • Stream tests verify reset forwarding preserves stream codes and does not forward session codes.
  • nix develop --command just fix
  • nix develop --command just check
  • nix develop --command just test
  • Focused error, stream, and reload tests: 54 passed, 0 failed.

Cross-Package Sync: updated the @moq/net documentation and subscribe example. No wire format changed.

(Written by GPT-5.6 Sol)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b416929a5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread js/net/src/error.ts Outdated
/** Which registry {@link code} belongs to. */
readonly source: RemoteSource;

constructor(code: number, options: { source: RemoteSource; cause?: unknown; reason?: string }) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Base this breaking constructor change on dev

This makes the exported RemoteError constructor's previously optional options argument required, but the reviewed commit is based directly on main (76a1aedd). If landed there, existing @moq/net consumers that call new RemoteError(code) receive a breaking change outside the designated breaking-release line. Rebase or retarget this change onto dev, which the repository explicitly reserves for signature changes to exported library APIs. (Written by GPT-5.6 Sol)

AGENTS.md reference: AGENTS.md:L184-L186

Useful? React with 👍 / 👎.

SessionCode.Unauthorized and StreamCode.DeliveryTimeout are both 2 in disjoint registries. A single RemoteError carrying an untyped number let reconnect logic and reset forwarding interpret a code against the wrong registry.

Replace it with SessionError and StreamError carrying branded SessionCode and StreamCode values. Race SETUP negotiation against the session close so an authorization rejection during the handshake reaches the terminal retry classifier, while stream resets remain retryable.

BREAKING CHANGE: RemoteError is replaced by SessionError and StreamError, and their code fields use distinct branded registry types.
@kixelated
kixelated force-pushed the claude/js-remote-error-registry branch from 7b41692 to a576462 Compare August 12, 2026 22:03
@kixelated kixelated changed the title fix(js/net)!: record which registry a RemoteError code came from fix(js/net)!: separate session and stream errors Aug 12, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a5764620d2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread js/net/src/error.ts
Comment on lines +56 to +57
/** A code from the stream reset registry. */
export type StreamCode = number & { readonly [STREAM_CODE]: true };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Expose a safe constructor for application codes

When a publisher uses an application-defined reset code from the documented 64+ range, the new opaque brand makes group.close(new StreamError(64)) fail type-checking, while StreamCode exposes only the fixed protocol constants and no public factory. Because publisher errors eventually reach stream.reset and only a StreamError preserves its code, applications can no longer send their own reset codes without an unsafe assertion; expose a validated constructor or factory for application codes. (Written by GPT-5.6 Sol)

AGENTS.md reference: AGENTS.md:L133-L135

Useful? React with 👍 / 👎.

Comment thread js/net/src/error.ts
*
* @public
*/
declare const SESSION_CODE: unique symbol;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Attach the registry documentation to the exported constant

The detailed registry comment now attaches to the private SESSION_CODE declaration rather than the exported SessionCode constant, leaving the public value undocumented in generated .d.ts and JSR API docs; the same displacement occurs for StreamCode. Move each registry comment directly above its exported constant and give the private brand declaration its own internal comment. (Written by GPT-5.6 Sol)

AGENTS.md reference: AGENTS.md:L92-L95

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant