Skip to content

Fix bare KeyError crash in _callback_RESP_NotFound / _callback_RESP_Unavailable - #320

Open
hainesdev wants to merge 5 commits into
tayler6000:masterfrom
hainesdev:bugfix/Issue-85
Open

Fix bare KeyError crash in _callback_RESP_NotFound / _callback_RESP_Unavailable#320
hainesdev wants to merge 5 commits into
tayler6000:masterfrom
hainesdev:bugfix/Issue-85

Conversation

@hainesdev

Copy link
Copy Markdown

Fixes #85

_callback_RESP_NotFound and _callback_RESP_Unavailable check if call_id not in self.calls and log a debug message, but (unlike the otherwise-identical _callback_RESP_OK, which already has the correct return) never actually return afterward -- so they fall through into self.calls[call_id]... and crash with a bare KeyError instead of the graceful handling the surrounding code clearly intended.

This matches the traceback in #85 exactly: a 404 arriving during registration lands in _callback_RESP_NotFound before the call/registration is in self.calls, and crashes with KeyError: '<call-id>'. It's also reachable from a normal call attempt: invite()'s own blocking response loop can hand either callback an early response (e.g. a 503 on a pre-auth probe INVITE) that arrives before VoIPPhone.call() has gotten around to registering the call in self.calls.

Fix adds the missing return to both, matching the pattern _callback_RESP_OK already uses correctly.

hainesdev and others added 5 commits August 7, 2026 00:13
Fixes tayler6000#85

[FIX] Fixed _callback_RESP_NotFound and _callback_RESP_Unavailable
      checking `if call_id not in self.calls` and logging a debug
      message, but never returning afterward, unlike the otherwise
      identical _callback_RESP_OK. Both fell through into
      self.calls[call_id]... and crashed with a bare KeyError.
      Reachable in normal operation: invite()'s own blocking response
      loop can hand either of these an early response (e.g. a 404 on
      a registration, or a 503 on a pre-auth probe INVITE) that
      arrives before the call/registration is registered in
      self.calls.
When the remote swaps its media source mid-call (e.g. Asterisk dialplan
playback -> a bridge) the new source picks a fresh random RTP timestamp
base while the sequence number stays contiguous. RTPPacketManager.write
then seek()s gigabytes into the buffer and playback never recovers.

Mirror the existing large-backward-jump guard: on a forward delta past the
same 100000-byte threshold, reset the buffer to the new packet as a fresh
reference point.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
pyVoIP could receive RFC 2833 DTMF but never send it. Add
RTPClient.send_dtmf() / VoIPCall.send_dtmf(): a queued burst of
telephone-event RTP packets (marker on the first, three redundant
end packets, frozen event timestamp) that pre-empts the audio stream
in trans() one 20 ms packet per iteration so tone timing is preserved.
No-op when the peer did not negotiate telephone-event.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The first attempt (b3d5dc2) compared each packet's timestamp to the buffer
base self.offset, which only ever decreases -- so a normally advancing
stream is eventually always >100000 past its own base and every packet
rebuilt the buffer, starving playback ~12s in.

Track self.last_offset (the previous write's timestamp) and only rebase
when a packet jumps far past *that* -- a genuine media-source switch, not
routine forward progress.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Register OPTIONS as a compatible method and reply to it (to the Via
sender, falling back to the configured server) so proxies that probe the
client with OPTIONS don't treat the registration as dead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KeyError: 'd4735e3a265e16eee03f59718b9b5d03@0.0.0.0:5060'

1 participant