Skip to content

fix(voice-call): verify call status with provider before loading stale calls#4325

Merged
steipete merged 1 commit intoopenclaw:mainfrom
garnetlyx:fix/voice-call-verify-stale-calls
Mar 3, 2026
Merged

fix(voice-call): verify call status with provider before loading stale calls#4325
steipete merged 1 commit intoopenclaw:mainfrom
garnetlyx:fix/voice-call-verify-stale-calls

Conversation

@garnetlyx
Copy link
Contributor

@garnetlyx garnetlyx commented Jan 30, 2026

Problem

When the gateway restarts, loadActiveCalls() reloads non-terminal calls from calls.jsonl. However, these calls may have already ended (e.g., Twilio timed them out, or webhook couldn't reach local URL) and are now stale. This causes the concurrent call limit to be reached with phantom calls.

Solution

  • Add getCallStatus() method to VoiceCallProvider interface
  • Implement for all providers (Twilio, Plivo, Telnyx, Mock) using guardedJsonApiRequest
  • On initialize(), verify each persisted non-terminal call with the provider before restoring
  • Skip calls the provider reports as terminal (completed, failed, etc.)
  • Skip calls older than maxDurationSeconds as a time-based fallback
  • Skip calls without providerCallId (can't verify)

Fixes from review feedback

  • P0 — Restart max-duration timers: Restored calls in answered+ state now get their hangup timers restarted with remaining time calculated from answeredAt
  • P1 — Transient error handling: Provider lookup failures (network, 5xx) return isUnknown: true → call is kept with timer fallback. Only 404/known-terminal statuses drop the call
  • P1 — Telnyx is_alive safety: Missing is_alive field treated as unknown (not terminal)
  • Security: All provider GET calls use guardedJsonApiRequest (SSRF-guarded)

Testing

  • 6 new unit tests covering verification scenarios (terminal, active, unknown, expired, no providerCallId, verification failure)
  • All 84 voice-call tests pass
  • pnpm tsgo + pnpm check clean

Test plan

  1. Initiate a call
  2. Kill the gateway before the call ends
  3. Restart the gateway
  4. Verify that stale calls are logged as skipped (provider status check)
  5. Verify that new calls can be initiated

@openclaw-barnacle openclaw-barnacle bot added the channel: voice-call Channel integration: voice-call label Jan 30, 2026
@garnetlyx garnetlyx force-pushed the fix/voice-call-verify-stale-calls branch 3 times, most recently from 0ce84d6 to a9ac6b4 Compare January 31, 2026 16:21
@openclaw-barnacle openclaw-barnacle bot added the docs Improvements or additions to documentation label Jan 31, 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.

4 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

garnetlyx pushed a commit to garnetlyx/openclaw that referenced this pull request Feb 3, 2026
…provider errors as non-terminal

- Start maxDuration timer for restored calls that are in active states (not terminal or initiated)
- Treat provider lookup errors (network, timeout, etc.) as unknown/non-terminal, keeping calls active
- Only treat definite terminal errors (404 not-found) as terminal, skipping those calls
- Fixes review comments on PR openclaw#4325
@garnetlyx garnetlyx force-pushed the fix/voice-call-verify-stale-calls branch from 828c2b9 to 3e794d6 Compare February 4, 2026 12:50
@openclaw-barnacle openclaw-barnacle bot removed the docs Improvements or additions to documentation label Feb 4, 2026
@mudrii

This comment was marked as spam.

…e calls

On gateway restart, persisted non-terminal calls are now verified with
the provider (Twilio/Plivo/Telnyx) before being restored to memory.
This prevents phantom calls from blocking the concurrent call limit.

- Add getCallStatus() to VoiceCallProvider interface
- Implement for all providers with SSRF-guarded fetch
- Transient errors (5xx, network) keep the call with timer fallback
- 404/known-terminal statuses drop the call
- Restart max-duration timers for restored answered calls
- Skip calls older than maxDurationSeconds or without providerCallId
@garnetlyx garnetlyx force-pushed the fix/voice-call-verify-stale-calls branch from 3e794d6 to ffa7c13 Compare March 2, 2026 08:55
@garnetlyx
Copy link
Contributor Author

Rewrote this PR from scratch on current main. The original branch was stale and had CI failures + unaddressed review feedback.

What changed in the rewrite:

  • Rebased on current main (no merge conflicts, clean CI baseline)
  • All provider getCallStatus() implementations use guardedJsonApiRequest (SSRF-guarded) instead of raw fetch()
  • P0 fix: Restored calls now get max-duration hangup timers restarted with remaining time calculated from answeredAt — original PR missed this entirely
  • P1 fix: Transient provider errors (network blips, 5xx) now return isUnknown: true and keep the call alive with timer fallback. Only definitive statuses (404, known terminal like completed/failed) drop the call. Original PR treated all errors as terminal which could incorrectly drop active calls
  • P1 fix: Telnyx — missing is_alive field now treated as unknown rather than terminal (original defaulted to false)
  • Added 6 new unit tests covering: terminal skip, active keep, unknown keep, max-age skip, no-providerCallId skip, verification-failure keep
  • All 84 voice-call tests pass, pnpm check clean

@steipete steipete merged commit fe14be2 into openclaw:main Mar 3, 2026
22 of 23 checks passed
@steipete
Copy link
Contributor

steipete commented Mar 3, 2026

Landed via local merge on main to preserve PR head ancestry and resolve conflicts.

  • Gate: pnpm lint && pnpm build && pnpm test
  • Land commit: PLACEHOLDER_LAND_SHA
  • Merge commit: PLACEHOLDER_MERGE_SHA

Thanks @garnetlyx!

@steipete
Copy link
Contributor

steipete commented Mar 3, 2026

SHA correction for landing note:

zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
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: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants