Skip to content

Playback stops after ~10s: GVS returns 403 for ranges past the first ~60s with ANDROID_VR client (likely missing GVS PO token) #1481

Open

Description

@willheisenberg

Context

  • Add-on Version: 7.4.4 (from repository.xbmc.org)
  • Kodi Version: 21.3 (21.3.0) Git:21.3-Omega, Linux ARM 64-bit
  • Kodi GUI Language: German (de_DE)
  • Operating System: LibreELEC 12.2.1 on Raspberry Pi 5, kernel 6.12.56
  • Operating System Language: German
  • inputstream.adaptive: 21.5.22.1
  • Not signed in to the add-on (own API key/OAuth credentials configured, but no account authorised)

Expected Behavior

Video plays through to the end.


Current Behavior

Playback starts, runs for roughly 10-15 seconds, then stops and returns to the listing.

The add-on's stream proxy receives 403 Forbidden from the googlevideo servers. The pattern is consistent: the initial range requests succeed, but every request for a byte range beyond roughly the first 60 seconds of the audio stream fails. inputstream.adaptive retries six times and gives up, which tears down the player well before playback actually reaches the 60 second mark.

All failing requests are made with Client: 28 (ANDROID_VR).

Over one affected session, 250 responses were 403 Forbidden and 26 were 400 Bad Request; the 400s appear when the add-on follows a Target: redirect to a different googlevideo host.

Affected itags were 140 (audio, mp4a) and 397 (video, AV1). Three different videos were affected in the same session, so it is not content specific.


Steps to Reproduce

  1. Start any video from a channel listing or search result.
  2. Playback begins normally.
  3. After ~10-15 seconds playback stops and Kodi returns to the previous screen.

Log

I am aware the template asks for a complete debug log, and I am sorry that I cannot supply one: debug logging was not enabled while the problem was occurring, and the problem has since stopped on its own without any change on my side. I would rather file this now than sit on it, because the relevant lines seem unambiguous and the timing lines up with a known upstream change (see below). I will turn on debug logging and verbose add-on logging and attach a full log immediately if it recurs.

Excerpt from kodi.log (public IP redacted, 127.0.0.1 is the add-on's own proxy):

2026-08-14 14:52:59.550 T:8676  warning <general>: [plugin.video.youtube] http_server:537(do_GET)
                                                   Stream proxy response not OK
                                                   Stream: ('<video-id>', '140') - ('audio', 'mp4')
                                                   Method: 'POST'
                                                   Server: 'rr2---sn-u1i5h5-5q.googlevideo.com'
                                                   Target: 'rr5---sn-5hne6nz6.googlevideo.com'
                                                   Status: 403 Forbidden
                                                   Client: 28 (ANDROID_VR)
                                                   Range:  'bytes=980184-1141774' (~60.57s)

2026-08-14 14:52:59.661 T:8676  warning <general>: [plugin.video.youtube] http_server:537(do_GET)
                                                   Stream proxy response not OK
                                                   Stream: ('<video-id>', '140') - ('audio', 'mp4')
                                                   Method: 'POST'
                                                   Server: 'rr5---sn-5hne6nz6.googlevideo.com'
                                                   Target: None
                                                   Status: 400 Bad Request
                                                   Client: 28 (ANDROID_VR)
                                                   Range:  'bytes=980184-1141774' (~60.57s)

Corresponding traceback:

Traceback (most recent call last):
  File ".../youtube_plugin/kodion/network/requests.py", line 442, in request
    response.raise_for_status()
  File ".../script.module.requests/lib/requests/models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url:
https://rr5---sn-5hne6nz6.googlevideo.com/videoplayback?...&itag=140&...&c=ANDROID_VR&...

And the resulting inputstream.adaptive failure:

error <general>: AddOnLog: inputstream.adaptive: [AS-17] Download failed, HTTP error 403:
  http://127.0.0.1:50152/youtube/stream?...&itag=140&...
warning <general>: AddOnLog: inputstream.adaptive: [AS-17] Segment download failed, attempt 2...
[... attempts 3 through 6 ...]
info <general>: CVideoPlayer::OnExit()

There are no LOGIN_REQUIRED or "Sign in to confirm you're not a bot" entries anywhere in the log, and no VPN, proxy or custom DNS is in use.


Additional Information

This looks like it may be the same change yt-dlp documented on 2026-08-02 in yt-dlp/yt-dlp#17348: the android_vr client now requires a GVS PO token for everything except format 18, and without it the video servers return 403. The symptom described there — initial data is served, then 403 — matches what I see.

As far as I can tell from reading the source, the add-on has no PO token handling (only visitorData), so I assume there is no configuration on my side that would work around this.

One secondary observation while trying to work around it locally, in case it is useful: in _process_adaptive_streams, streams are written with

mime_group[itag] = quality_group[itag] = details

without checking whether the itag is already present, so a later client group overwrites the URLs of earlier ones. Since android_vr is the last entry in _client_groups, its URLs win for every itag it provides — including audio itag 140. That is why restricting the stream features (for example disabling AV1 so that itag 397 is not selected) does not help: the audio URL still comes from android_vr and still fails. The HLS path at lines ~1226 and ~1264 does guard with if itag in stream_list: continue, so the behaviour differs between the two paths.

Happy to test any patch or test build, and to provide a full debug log if it comes back.

Activity

  1. popcornmix commented on Aug 14, 2026

    @popcornmix

    I've been hitting this. Often stops after 30s or 60s.
    But intermittent, sometimes the same video plays to the end.
    Caught in a debug log.

    2026-08-14 17:18:46.589 T:3600  warning <general>: [plugin.video.youtube] http_server:537(do_GET)
                                                       Stream proxy response not OK
                                                       Stream: ('rdYBjpylJUQ', '140') - ('audio', 'mp4')
                                                       Method: 'POST'
                                                       Server: 'rr4---sn-25glenez.googlevideo.com'
                                                       Target: None
                                                       Status: 403 Forbidden
                                                       Client: 28 (ANDROID_VR)
                                                       Range:  'bytes=971640-1133224' (~60.03s)
    
  2. BehnK commented on Aug 17, 2026

    @BehnK

    I can confirm that Serph91P's fix has worked with this issue on my Kodi Omega intallation of 7.4.4

  3. Marxiboy commented on Aug 19, 2026

    @Marxiboy

    I also confirm this fixed my Khadas Vim 4 21.3.

  4. lbriais commented on Aug 19, 2026

    @lbriais

    Same issue here, sometimes it works and most of the times it fails...
    I got the exact same error messages, seeing the exact same:

    2026-08-19 17:04:27.489 T:23491 warning <general>: [plugin.video.youtube] http_server:537(do_GET)
                                                       Stream proxy response not OK
                                                       Stream: ('x_6g9Ts53y4', '140') - ('audio', 'mp4')
                                                       Method: 'POST'
                                                       Server: 'rr2---sn-p5qlsn76.googlevideo.com'
                                                       Target: None
                                                       Status: 403 Forbidden
                                                       Client: 28 (ANDROID_VR)
                                                       Range:  'bytes=973699-1135482' (~60.16s)
    

    The point is... this is not kodi for android... this a linux box (osmc)... Thus I doubt I will be able to "refresh the android vr client profile"...

  5. AIsNeverDie commented on Aug 19, 2026

    @AIsNeverDie

    "android_vr" is dead.

  6. lbriais commented on Aug 19, 2026

    @lbriais

    Ok my bad, I finally understood this android stuff was just display, and reading the PR from Serph91P I just had to patch a single file to update the client definition (not the client)...

    And yes as weird as it sounds it fixed the issue !! Thanks Serph91P 👍 !

    Hopefully it will eventually make its way to the next release to avoid having to patch it again !

  7. bierbaron2 commented on Aug 19, 2026

    @bierbaron2

    I'm using 7.2.0+beta.5 (because I don't like the short videos to be separated) and the fix isn't working. Can somebody please provide a fix for this version?

    It works with 7.4.4

  8. repitah commented on Aug 21, 2026

    @repitah

    See the patched file in the pull request: #1482

  9. RobjSky commented on Aug 22, 2026

    @RobjSky

    I applied Serph91P's fix and it has worked for me. Youtube is running fine again on Nvidia Shield with Omega and addon 7.4.4

  10. calvin4242 commented on Aug 25, 2026

    @calvin4242

    Thank you 👍it works for me with 21.3 Omega and addon 7.4.4

  11. mrfoobar1 commented on Aug 27, 2026

    @mrfoobar1

    I applied the fix on 21.3 Omega and addon 7.4.4 and it fix the issue!

  12. rsantellan commented on Aug 28, 2026

    @rsantellan

    I applied the fix on 21.2-Omega with 7.4.4 and it also fix it! Thank you very much!

  13. CarstenG2 commented on Aug 30, 2026

    @CarstenG2

    Measurements on the 403 cutoff, plus a note on why #1482 works

    Confirming this on 7.4.4 / Kodi 21.3 / inputstream.adaptive 21.5.23 (Windows x64, not signed in). I measured the cutoff outside of Kodi and ended up with a result that may be worth knowing before #1482 is merged, because the mechanism looks different from what the PR assumes.


    1. The cutoff is a fixed position in the stream

    Requesting the audio stream directly with the client's own headers and bisecting the byte offset at which GVS switches from 206 to 403:

    video reachable cutoff
    A 48.4 % of 955556 B ~75.8s of 156.5s
    B 48.0 % of 965753 B ~76.0s of 158.2s
    C 62.9 % of 741031 B ~76.3s of 121.3s

    Three different file sizes and durations, but the cutoff lands at roughly 76 seconds every time. On the higher-bitrate audio track (itag 140) it was ~70s.

    What does not matter: the ip bound into the signed URL (it matched), the request headers, Origin/Referer, GET vs POST, and the start of the requested range. bytes=970768-987151 returns 206, while bytes=970768-1132492 returns 403 — only how far the range reaches into the stream decides it.

    2. Why the picture freezes while audio keeps playing

    From a debug log, the order of events is:

    1. inputstream.adaptive requests the audio segment past the cutoff, gets 403, retries six times, gives up.
    2. After that it stops requesting any segments — video included.
    3. CDVDVideoCodecFFmpeg::GetPicture - eof hw accel, immediately followed by CVideoPlayerVideo - Stillframe detected.
    4. The audio buffer outlives the video buffer, so sound continues and ActiveAE - large audio sync error grows 1:1 with wall clock.

    So audio is what actually breaks, but a frozen picture is what users see first.

    3. #1482 fixes it, but not for the stated reason

    The PR assumes the refreshed ANDROID_VR profile yields usable stream URLs again. In my measurements it does not: with the Pico 4 identity the player response comes back with serverAbrStreamingUrl and adaptive formats carrying neither url nor signatureCipher — SABR delivery, unusable for the add-on. Forcing a URL out of it and requesting past the cutoff still returns 403.

    What actually changes is the fallthrough:

    client used for streams result
    before 78x c=ANDROID_VR truncated at ~76s
    after #1482 106x c=ANDROID complete

    The client that ends up serving is android_testsuite_params. So the PR works by making android_vr fail cleanly rather than by repairing it.

    4. Profile matrix

    Every profile in request_client.py, with and without the player params, headers and body rendered from the templates in the file:

    profile params result
    android 8AEB no audio formats
    android 2AMB complete
    android_testsuite_params 2AMB complete
    ios (none) truncated (403)
    ios 2AMB complete
    ios_testsuite_params 2AMB complete
    android_vr (Pico) (none) SABR, no URL
    android_vr (Pico) 2AMB truncated (403)
    tv, tv_unplugged, tv_embed, web, v1, mweb either UNPLAYABLE / ERROR
    android_youtube_tv, ios_youtube_tv either LOGIN_REQUIRED
    android_testsuite 2AMB UNPLAYABLE
    android_embedded either player request 404

    Two pairs isolate the cause, since only the params value differs within each pair:

    • android fails with 8AEB and plays through completely with 2AMB.
    • ios truncates without params and plays through completely with 2AMB.

    So the deciding factor is params=2AMB, not the user agent or the client id. No profile delivers a complete stream without it. And android_vr cannot be rescued even with 2AMB — the VR loophole looks closed rather than stale.

    5. Suggestion

    If the VR profile no longer yields usable URLs on any identity, dropping it from the candidate list is more direct than swapping in another headset, and it states the intent:

    +            # Removed - GVS returns 403 past ~76s of the stream
                 ('auth_enabled|no_kids|av1|vp9.2|avc1|surround_sound', (
    -                'android_vr',
                 )),

    The group keeps an empty tuple, matching the existing ('mpd', ()) and ('ask', ()) entries, and request_client.py stays untouched.

    I ran both variants against the same video. Identical outcome: full 157s playback, clean OnPlayBackEnded, video track complete (23964578 / 23964579 bytes), audio track complete (2533042 / 2533043 bytes), zero Stillframe detected, zero sync errors, zero 403s in the stream proxy. Both end up on android_testsuite_params.

    6. One thing worth flagging

    Everything now rests on params=2AMB. Decoded it is a tiny protobuf:

    8AEB ('default')   -> f0 01 01 -> field 30, varint = 1
    2AMB ('testsuite') -> d8 03 01 -> field 59, varint = 1
    

    A single flag, differing only in field number. If that one gets restricted too, neither a profile swap nor a fresh identity will help — so it may be worth treating ios_testsuite_params as an explicit backup, since it also delivers complete streams and is already in the candidate list.

    Happy to open a PR for the removal if that direction is preferred.

  14. Fludizz commented on Aug 30, 2026

    @Fludizz

    @CarstenG2 the action of removing (or commenting out) the 'android_vr' line might fix the playback issue, however, it does introduce a new issue: Updating the Watch History in your Youtube account breaks, as I mentioned in #1483 (comment)

  15. willheisenberg commented on Sep 10, 2026

    @willheisenberg
    Author

    Follow-up from the reporter: it came back on 2026-09-04 on the same setup
    (Raspberry Pi 5, LibreELEC 12.2.1, Kodi 21.3, add-on 7.4.4,
    inputstream.adaptive 21.5.24.1, not signed in). Same pattern: tv/tv_unplugged
    fail with UNPLAYABLE, ANDROID_VR URLs return 403 after a few seconds.

    Commenting out 'android_vr' in _client_groups (player_client.py) fixed it.
    No 403s or inputstream.adaptive download failures in the logs since 2026-09-08.

    As I'm not signed in, I can't confirm anything regarding the watch history
    side effect from #1483. Happy to test the _use_adaptive: False variant
    suggested in #1482 if that helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions