Skip to content

Commit 03ce23d

Browse files
fix(call): prevent stale-data VideoCall mount and guard startRecording (#1226)
Key Stage by stage.id to eliminate the one-render window where player.stage updates before useStage(), causing VideoCall to mount with stale discussion config during stage transitions. Also guard startRecording() return value to prevent TypeError crash if the Daily SDK returns undefined in a transitional state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 850993d commit 03ce23d

2 files changed

Lines changed: 28 additions & 22 deletions

File tree

client/src/Game.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export function Game() {
136136
<Profile />
137137
</div>
138138
<div className="absolute top-12 left-0 right-0 bottom-0 m-2">
139-
<Stage />
139+
<Stage key={stage.id} />
140140
</div>
141141
</>
142142
);

client/src/call/hooks/useCallStartSignaling.js

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,28 +34,34 @@ export function useCallStartSignaling(callObject, recordingEnabled, stageId) {
3434
const startRecordingIfNeeded = () => {
3535
if (recordingEnabled && !recordingStartedRef.current) {
3636
recordingStartedRef.current = true;
37-
callObject.startRecording({ type: "raw-tracks" }).then(
38-
() => console.log("[Recording] Started raw-tracks recording from client"),
39-
(err) => {
40-
console.warn("[Recording] Failed to start recording:", err.message);
41-
recordingStartedRef.current = false;
37+
const result = callObject.startRecording({ type: "raw-tracks" });
38+
if (result && typeof result.then === "function") {
39+
result.then(
40+
() => console.log("[Recording] Started raw-tracks recording from client"),
41+
(err) => {
42+
console.warn("[Recording] Failed to start recording:", err.message);
43+
recordingStartedRef.current = false;
4244

43-
// Defer Sentry alert: wait 5s and check if another participant
44-
// successfully started recording (indicated by recording-started
45-
// event setting recordingConfirmedRef). This avoids false alarms
46-
// when one client fails but another succeeds — Daily broadcasts
47-
// recording-started to all participants regardless of who initiated.
48-
const timer = setTimeout(() => {
49-
if (!recordingConfirmedRef.current) {
50-
Sentry.captureMessage("Recording not started for stage", {
51-
level: "error",
52-
extra: { triggeringError: err.message, stageId },
53-
});
54-
}
55-
}, 5000);
56-
pendingTimers.push(timer);
57-
}
58-
);
45+
// Defer Sentry alert: wait 5s and check if another participant
46+
// successfully started recording (indicated by recording-started
47+
// event setting recordingConfirmedRef). This avoids false alarms
48+
// when one client fails but another succeeds — Daily broadcasts
49+
// recording-started to all participants regardless of who initiated.
50+
const timer = setTimeout(() => {
51+
if (!recordingConfirmedRef.current) {
52+
Sentry.captureMessage("Recording not started for stage", {
53+
level: "error",
54+
extra: { triggeringError: err.message, stageId },
55+
});
56+
}
57+
}, 5000);
58+
pendingTimers.push(timer);
59+
}
60+
);
61+
} else {
62+
console.warn("[Recording] startRecording() returned non-Promise; call may be in transitional state");
63+
recordingStartedRef.current = false;
64+
}
5965
}
6066
};
6167

0 commit comments

Comments
 (0)