Skip to content

Commit ccd892f

Browse files
Apollon77claude
andauthored
fix(node): expose subscriptions on ClientNodeInteraction wrapper (#3694)
CommissioningClient assigns `node.interaction as ClientInteraction` to `peer.interaction`, but `node.interaction` is a `ClientNodeInteraction` wrapper that did not expose `subscriptions`. PeerAddressMonitor's address-migration path then dereferenced `interaction.subscriptions`, crashing with "Cannot read properties of undefined (reading 'closeForPeer')" whenever a session migrated to a discovered IP. Add a `subscriptions` getter on `ClientNodeInteraction` that resolves the singleton from the node environment, matching what the inner `ClientInteraction` returns. Tighten `ClientInteraction.environment` from `protected` to `#environment` since no subclass needs it. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 43d0df6 commit ccd892f

2 files changed

Lines changed: 8 additions & 3 deletions

File tree

packages/node/src/node/client/ClientNodeInteraction.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
ClientRead,
1919
ClientSubscribe,
2020
ClientSubscription,
21+
ClientSubscriptions,
2122
ClientWrite,
2223
DecodedInvokeResult,
2324
Interactable,
@@ -165,6 +166,10 @@ export class ClientNodeInteraction implements Interactable<ActionContext> {
165166
return this.#interaction.probe(options);
166167
}
167168

169+
get subscriptions(): ClientSubscriptions {
170+
return this.#node.env.get(ClientSubscriptions);
171+
}
172+
168173
get #interaction() {
169174
if (this.#node.construction.status !== Lifecycle.Status.Active) {
170175
throw new ImplementationError(

packages/protocol/src/action/client/ClientInteraction.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ const DEFAULT_MINIMUM_RESPONSE_TIMEOUT_WITH_FAILSAFE = Seconds(30);
108108
export class ClientInteraction<
109109
SessionT extends InteractionSession = InteractionSession,
110110
> implements Interactable<SessionT> {
111-
protected readonly environment: Environment;
111+
readonly #environment: Environment;
112112
readonly #lifetime: Lifetime;
113113
readonly #exchangeProvider: ExchangeProvider;
114114
readonly #interactions = new BasicSet<Read | Write | Invoke | Subscribe | ClientBdxRequest>();
@@ -125,7 +125,7 @@ export class ClientInteraction<
125125
#nextCommandRef = 1;
126126

127127
constructor({ environment, abort, sustainRetries, exchangeProvider, address, network }: ClientInteractionContext) {
128-
this.environment = environment;
128+
this.#environment = environment;
129129
this.#exchangeProvider = exchangeProvider ?? environment.get(ExchangeProvider);
130130
if (environment.has(ClientSubscriptions)) {
131131
this.#subscriptions = environment.get(ClientSubscriptions);
@@ -180,7 +180,7 @@ export class ClientInteraction<
180180

181181
get subscriptions() {
182182
if (this.#subscriptions === undefined) {
183-
this.#subscriptions = this.environment.get(ClientSubscriptions);
183+
this.#subscriptions = this.#environment.get(ClientSubscriptions);
184184
}
185185
return this.#subscriptions;
186186
}

0 commit comments

Comments
 (0)