Skip to content

fix(voice-call): auto-end call on media stream disconnect#18435

Merged
steipete merged 4 commits intoopenclaw:mainfrom
JayMishra-source:fix/voice-auto-end-disconnect
Feb 16, 2026
Merged

fix(voice-call): auto-end call on media stream disconnect#18435
steipete merged 4 commits intoopenclaw:mainfrom
JayMishra-source:fix/voice-auto-end-disconnect

Conversation

@JayMishra-source
Copy link
Contributor

@JayMishra-source JayMishra-source commented Feb 16, 2026

Summary

  • When a Twilio media stream disconnects (caller hangs up, network drop, etc.), the call was left in active state indefinitely — a "stuck call"
  • Now automatically ends the call when onDisconnect fires, matching expected lifecycle
  • Prevents resource leaks and blocked concurrent call slots

Context

Running OpenClaw with Twilio voice calls in a Docker deployment. When the media stream disconnects (common with network issues or normal hangup), the call record stays active. Over time, this accumulates stuck calls that must be manually cleared.

The fix adds manager.endCall() in the onDisconnect handler before unregistering the stream, so the call is properly cleaned up.

Test plan

  • Make a voice call, hang up normally → call should end immediately
  • Simulate network drop during active call → call should end when stream disconnects
  • Verify no duplicate end-call attempts (endCall is idempotent)

🤖 Generated with Claude Code

Greptile Summary

Adds automatic call cleanup when a Twilio media stream disconnects, preventing "stuck calls" that remain active indefinitely after the WebSocket closes (e.g., caller hangup, network drop). The fix inserts manager.endCall() in the onDisconnect handler before stream unregistration.

  • The change is safe: endCall is idempotent — it returns early if the call is already in a terminal state or missing from activeCalls, and Twilio's hangupCall uses allowNotFound: true for the REST API call
  • Race conditions with the call.ended webhook path are handled correctly — both paths converge on the same endCall which guards against double cleanup
  • Ordering with unregisterCallStream is correct — endCall/hangupCall cleans up auth tokens and webhook URLs, while unregisterCallStream cleans up the stream mapping with no overlap
  • Minor style note: the .catch(() => {}) silently swallows errors, unlike similar fire-and-forget patterns elsewhere in this codebase that log warnings

Confidence Score: 4/5

  • This PR is safe to merge — the fix is minimal, well-targeted, and the auto-end logic is protected by idempotency guards.
  • Score of 4 reflects a clean, low-risk bug fix. The change is small (7 lines), correctly leverages existing idempotency in endCall, and handles race conditions with webhook events safely. Only deducting one point for the silent error swallowing pattern which deviates from codebase conventions, though it's not a functional issue.
  • No files require special attention. The single changed file (extensions/voice-call/src/webhook.ts) has a minor style suggestion but no functional issues.

Last reviewed commit: c0cd3c2

(4/5) You can add custom instructions or style guidelines for the agent here!

When a Twilio media stream disconnects (e.g., caller hangs up or
network drops), the call object was left in an active state indefinitely.
This caused "stuck calls" that consumed resources and blocked new calls.

Now calls are automatically ended when their media stream closes,
matching the expected lifecycle behavior.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@openclaw-barnacle openclaw-barnacle bot added channel: voice-call Channel integration: voice-call size: XS labels Feb 16, 2026
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

const disconnectedCall = this.manager.getCallByProviderCallId(callId);
if (disconnectedCall) {
console.log(`[voice-call] Auto-ending call ${disconnectedCall.callId} on stream disconnect`);
void this.manager.endCall(disconnectedCall.callId).catch(() => {});
Copy link
Contributor

Choose a reason for hiding this comment

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

Silent error swallowing on endCall
The .catch(() => {}) silently discards all errors. While endCall is effectively idempotent (returns early for terminal states or missing calls), unexpected failures (e.g., network errors during the Twilio API call to complete the call) would be invisible. Other fire-and-forget patterns in this codebase log a warning on failure (e.g., events.ts:127-129, webhook.ts:124-126). Consider logging the error for observability:

Suggested change
void this.manager.endCall(disconnectedCall.callId).catch(() => {});
void this.manager.endCall(disconnectedCall.callId).catch((err) => {
console.warn(`[voice-call] Failed to auto-end call ${disconnectedCall.callId}:`, err);
});

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/voice-call/src/webhook.ts
Line: 159:159

Comment:
**Silent error swallowing on endCall**
The `.catch(() => {})` silently discards all errors. While `endCall` is effectively idempotent (returns early for terminal states or missing calls), unexpected failures (e.g., network errors during the Twilio API call to complete the call) would be invisible. Other fire-and-forget patterns in this codebase log a warning on failure (e.g., `events.ts:127-129`, `webhook.ts:124-126`). Consider logging the error for observability:

```suggestion
          void this.manager.endCall(disconnectedCall.callId).catch((err) => {
            console.warn(`[voice-call] Failed to auto-end call ${disconnectedCall.callId}:`, err);
          });
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

JayMishra-source and others added 3 commits February 16, 2026 10:33
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Address review feedback: log a warning when endCall fails on stream
disconnect instead of silently discarding the error.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@steipete steipete merged commit 95024d1 into openclaw:main Feb 16, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: voice-call Channel integration: voice-call size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants