You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AudioPipeline.record() silently creates and returns a valid-looking but empty WAV file when recording starts while the audio pipeline is not running. CallSession.record() delegates directly to this method, so a PBX/voicemail flow can report success even though no audio transport was active and no caller audio could have been captured.
This is a robustness/DX gap for the voice and voicemail roadmap: a missing audio bridge, failed CPCMREG, or disconnected audio serial port should fail closed with an actionable error, not produce a 44-byte empty WAV that looks like a saved recording.
$ PYTHONPATH=. uv run --no-project --with pyserial-asyncio python - <<'PY'
import asyncio, os, tempfile, wave
from callstack.events.bus import EventBus
from callstack.transport.mock import MockTransport
from callstack.voice.audio import AudioPipeline
async def main():
out = os.path.join(tempfile.gettempdir(), 'callstack-record-not-running.wav')
try:
os.remove(out)
except FileNotFoundError:
pass
pipeline = AudioPipeline(MockTransport(), EventBus())
result = await pipeline.record(out, max_duration=0.2)
with wave.open(result, 'rb') as wf:
print('record_returned:', result)
print('pipeline_running:', pipeline.running)
print('frames:', wf.getnframes())
print('channels:', wf.getnchannels())
print('rate:', wf.getframerate())
print('file_bytes:', os.path.getsize(result))
asyncio.run(main())
PY
record_returned: /var/folders/n7/73kdk745271b818g855__bg00000gn/T/callstack-record-not-running.wav
pipeline_running: False
frames: 0
channels: 1
rate: 8000
file_bytes: 44
Affected code
callstack/voice/audio.py:65-112 opens the output WAV before checking self._running; if _running is false, the while self._running and ... loop never executes and the method still returns the output path.
callstack/voice/service.py:300-309 exposes this behavior through CallSession.record(...).
tests/test_audio.py covers normal recording and max-duration behavior but not the inactive-pipeline case.
Expected behavior
Starting a recording while the audio pipeline is not running should return an explicit failure before creating a misleading successful recording artifact. Callers should be able to distinguish "recorded silence from an active call" from "audio bridge was unavailable".
Suggested fix direction
Validate AudioPipeline.running before opening/writing the WAV, and raise a dedicated or existing audio/transport error with a redacted/actionable message when inactive.
Optionally validate max_duration > 0 at the same boundary, because non-positive durations currently have similar empty-file semantics.
Add tests for inactive-pipeline recording and, if error classes are adjusted, CallSession.record() propagation.
Summary
AudioPipeline.record()silently creates and returns a valid-looking but empty WAV file when recording starts while the audio pipeline is not running.CallSession.record()delegates directly to this method, so a PBX/voicemail flow can report success even though no audio transport was active and no caller audio could have been captured.This is a robustness/DX gap for the voice and voicemail roadmap: a missing audio bridge, failed
CPCMREG, or disconnected audio serial port should fail closed with an actionable error, not produce a 44-byte empty WAV that looks like a saved recording.Evidence
Baseline on
mainis healthy:Minimal no-hardware repro:
Affected code
callstack/voice/audio.py:65-112opens the output WAV before checkingself._running; if_runningis false, thewhile self._running and ...loop never executes and the method still returns the output path.callstack/voice/service.py:300-309exposes this behavior throughCallSession.record(...).tests/test_audio.pycovers normal recording and max-duration behavior but not the inactive-pipeline case.Expected behavior
Starting a recording while the audio pipeline is not running should return an explicit failure before creating a misleading successful recording artifact. Callers should be able to distinguish "recorded silence from an active call" from "audio bridge was unavailable".
Suggested fix direction
AudioPipeline.runningbefore opening/writing the WAV, and raise a dedicated or existing audio/transport error with a redacted/actionable message when inactive.max_duration > 0at the same boundary, because non-positive durations currently have similar empty-file semantics.CallSession.record()propagation.Acceptance criteria
AudioPipeline.record()fails before writing a success-looking WAV whenrunningis false.CallSession.record()propagates the failure so PBX/voicemail handlers can report or recover.max_durationis rejected rather than producing a 44-byte WAV.Verification gates