Skip to content

fix(js): drop a headless subgroup before FIRST_OBJECT (backport #5019) - #5120

Merged
kixelated merged 1 commit into
releasefrom
backport/js-headless-subgroup
Oct 10, 2026
Merged

kixelated merged 1 commit into
releasefrom
backport/js-headless-subgroup

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Backport of #5019 (321fc0274 on main) to release, for the 0.4.x @moq/net line. The 0.4.3 bump in #5119 should merge after this.

Problem

On drafts 14-17 the subgroup header has no FIRST_OBJECT bit, so decode forces firstObject on. A stream that starts at the live edge carries a non-zero first object delta, Frame.decode refuses it, and the group fails, which resets the subscription. This is the rust-to-js failure through moqx (draft-16) and moxygen (draft-14). Draft-18+ peers are unaffected: a cleared bit already takes the drop path.

Adaptation

main throws a new ObjectIdGap from Frame.decode and drops the stream in the catch. release already peeks the first object id when FIRST_OBJECT is clear, so this backport runs that same peek when !hasFirstObjectBit(version) instead of cherry-picking the catch path. A non-zero first id drops the stream and the subscription stays up for the next group.

An empty stream on drafts 14-17 still yields an empty group, matching main. A gap after the first object still fails that group, and a draft-18 header that claims object 0 and then starts elsewhere still fails it.

Public API and wire impact

  • No public API change. hasFirstObjectBit is exported within ietf/object.ts only; it is not re-exported from the package entry.
  • No wire change.

Tests

  • Ported the regression test a draft without FIRST_OBJECT drops a subgroup that starts mid-group (draft-16). It fails on release without the fix (group 3 is opened and failed) and passes with it.
  • bun test in js/net: 1114 pass, 0 fail.
  • just check against origin/release: pass.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

On drafts 14-17 the subgroup header has no FIRST_OBJECT bit, so a stream
that starts at the live edge reached Frame.decode with a non-zero first
delta and failed the group. Run release's existing first-object peek on
those drafts too, and drop the stream so the subscription resumes at the
next group.

(cherry picked from commit 321fc02, adapted)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-09T22:01:25.127861Z 1afd492 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@kixelated

Copy link
Copy Markdown
Collaborator Author

Automated review of 1afd492a (backport of #5019 to release)

The adaptation is sound: on drafts 14-17 the subscriber now peeks the first object id, drops a stream whose first id is non-zero (keeping the subscription up), and still treats an empty stream as an empty group. Draft-18+ behavior is unchanged since legacy is false there. The ported test covers the drop, the next group at object 0, and a later gap still failing only that group.

No blocking issues.

Non-blocking:

  • subscriber.test.ts subscribeTrack: ALPN selection maps any non-DRAFT_16 version to ALPN.DRAFT_19. Fine for today's callers, but a future draft-14/15/17 test would silently negotiate the wrong ALPN. A small version→ALPN map (or a throw for unsupported versions) would make this harder to misuse.
  • There's no draft-16 test for the empty-stream case (legacy && id === undefined yields an empty group). It's the one branch this backport adds that isn't exercised; a two-line test would lock it in.
  • CI (Check, Test, Replay) is still pending on this head. Per the body, chore(js): bump @moq/net to 0.4.3 on release #5119 (0.4.3 bump) should merge after this one.

Verdict: MERGE once CI is green.

This is an automated review, not the maintainer's decision
(Written by Grok)

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed commit: 1afd492

No new actionable defect found. Reusing the existing non-consuming peek is a suitably small release backport: js/net/src/ietf/subscriber.ts:1053–1068 drops a legacy non-zero first ID before opening the group, preserves clean empty groups, and leaves later gaps and draft-18+ contradictions on the failure path. The finally block still accounts for dropped streams. The version predicate matches header decoding; package exports and wire encoding remain unchanged.

The new regression distinguishes dropping from opening a failed group and checks recovery plus later-gap handling. I agree with the non-blocking empty-stream test suggestion already raised here; no duplicate inline comment.

Verification limits: GitHub-only static review of all three changed files, surrounding receive/Reader logic, and package exports. I did not run builds, tests, or live relay interoperability. Check, Release JS, and Audio quality workflows were still running at publication.

@kixelated
kixelated merged commit 71cdb96 into release Oct 10, 2026
5 checks passed
@kixelated
kixelated deleted the backport/js-headless-subgroup branch October 10, 2026 00:26
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