Skip to content

Preserve underlying error when InternetSource fails to decode#2857

Open
balloob wants to merge 6 commits into
postlund:masterfrom
balloob:raop-internet-source-error-context
Open

Preserve underlying error when InternetSource fails to decode#2857
balloob wants to merge 6 commits into
postlund:masterfrom
balloob:raop-internet-source-error-context

Conversation

@balloob

@balloob balloob commented May 25, 2026

Copy link
Copy Markdown

Summary

When streaming over HTTP via RAOP, the underlying error that caused a stream to fail was being swallowed. Users only saw miniaudio.DecodeError: ('failed to init decoder', -1) with no hint of the real cause (HTTP 401, connection error, timeout, etc.).

Two issues in pyatv/protocols/raop/audio_source.py:

  1. PatchedIceCastClient._stream_wrapper captured download exceptions as str(ex) and logged them at DEBUG. The actual exception type and traceback were lost.
  2. InternetSource.open checked source.error_message immediately after miniaudio.stream_any raised DecodeError, racing the download thread. If miniaudio failed before the download thread had recorded its error, the real cause was discarded and only the DecodeError propagated.

Changes

  • Store the actual exception (source.error: Optional[BaseException]) instead of a string, and log it at WARNING.
  • On DecodeError, explicitly close the source first (joining the download thread) so any HTTP/network error is captured before we inspect it.
  • Raise ProtocolError(...) from source.error so the original cause is preserved in the traceback chain.

If the URL truly returns valid audio that miniaudio can't decode, the DecodeError is still re-raised unchanged.

Motivation

This helps diagnose issues like home-assistant/core#169164 where the actual cause (e.g. an HTTP error from the media source) was masked by a generic failed to init decoder message.

🤖 Generated with Claude Code

balloob and others added 2 commits May 25, 2026 04:35
When streaming over HTTP via RAOP, the PatchedIceCastClient download
thread captured exceptions as strings and logged them at DEBUG level,
and InternetSource.open only inspected that string without waiting
for the download thread to finish. If miniaudio raised DecodeError
before the download thread had recorded its error, the real cause
(e.g. HTTP 401, connection error, timeout) was discarded and the
caller only saw "failed to init decoder".

Store the actual exception, wait for the download thread to settle on
DecodeError, and chain the original exception via "raise ... from" so
the underlying cause is preserved in tracebacks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@postlund

Copy link
Copy Markdown
Owner

@balloob flake8 issue is flaking out

Comment thread pyatv/protocols/raop/audio_source.py Outdated
Co-authored-by: Quentame <polletquentin74@me.com>
Comment thread pyatv/protocols/raop/audio_source.py
balloob and others added 2 commits June 3, 2026 05:30
Co-authored-by: Quentame <polletquentin74@me.com>
Refactor error handling in audio source decoding.
@balloob

balloob commented Jun 6, 2026

Copy link
Copy Markdown
Author

@postlund could we get a release with this fix?

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.

4 participants