What happens
The SDK never inspects msg === 'nosub'. The only discriminator on a DDP response is the presence of an error field, so the two refusal shapes a server can send take opposite paths through the subscription bookkeeping:
- A
nosub carrying an error rejects the send with a DDPError, and subscribe forgets the entry under that id (the ADR-0004 rule).
- A
nosub without an error settles the wait as a success with no subs, so subscribe writes nothing new but also forgets nothing — an existing entry survives, unconfirmed.
Measured against the real driver: after an errored nosub both recorded entries were gone (subscribeAll re-sent nothing); after an errorless one both survived.
The real Rocket.Chat server sends the destructive shape. Meteor itself does not check auth on sub; the publication does, and Rocket.Chat's stream-notify-user rule is Boolean(this.userId) && this.userId === userId, which throws not-allowed and produces { msg: 'nosub', id, error: {...} }. The EE ddp-streamer behaves the same.
Question
Should the two shapes keep taking opposite paths? If the distinction is deliberate it deserves to be stated and pinned; if it is an accident of where the error field is read, the paths should be unified.
What happens
The SDK never inspects
msg === 'nosub'. The only discriminator on a DDP response is the presence of an error field, so the two refusal shapes a server can send take opposite paths through the subscription bookkeeping:nosubcarrying an error rejects thesendwith aDDPError, andsubscribeforgets the entry under that id (the ADR-0004 rule).nosubwithout an error settles the wait as a success with nosubs, sosubscribewrites nothing new but also forgets nothing — an existing entry survives, unconfirmed.Measured against the real driver: after an errored
nosubboth recorded entries were gone (subscribeAllre-sent nothing); after an errorless one both survived.The real Rocket.Chat server sends the destructive shape. Meteor itself does not check auth on
sub; the publication does, and Rocket.Chat'sstream-notify-userrule isBoolean(this.userId) && this.userId === userId, which throwsnot-allowedand produces{ msg: 'nosub', id, error: {...} }. The EEddp-streamerbehaves the same.Question
Should the two shapes keep taking opposite paths? If the distinction is deliberate it deserves to be stated and pinned; if it is an accident of where the error field is read, the paths should be unified.