pear-devs/pear-desktop · error · Error

[${playabilityStatus.status}] ${playabilityStatus.reason}

Error message

[${playabilityStatus.status}] ${playabilityStatus.reason}

What it means

When YouTube reports playability status LOGIN_REQUIRED, the downloader tries to bypass it by re-fetching track info through the Android TV client (getAndroidTvInfo). If that fallback still reports LOGIN_REQUIRED, it throws an error formatted as '[LOGIN_REQUIRED] <reason>' (typically 'Sign in to confirm your age' or bot-detection messages).

Source

Thrown at src/plugins/downloader/main/index.ts:388

  metadata.trackId = trackId;

  const dir =
    playlistFolder || config.downloadFolder || app.getPath('downloads');
  const name = `${metadata.artist ? `${metadata.artist} - ` : ''}${
    metadata.title
  }`;
  setName(name);

  let playabilityStatus = info.playability_status;
  let bypassedResult: YT.VideoInfo;
  if (playabilityStatus?.status === 'LOGIN_REQUIRED') {
    // Try to bypass the age restriction
    bypassedResult = await getAndroidTvInfo(id);
    playabilityStatus = bypassedResult.playability_status;

    if (playabilityStatus?.status === 'LOGIN_REQUIRED') {
      throw new Error(
        `[${playabilityStatus.status}] ${playabilityStatus.reason}`,
      );
    }

    info = bypassedResult;
  }

  if (playabilityStatus?.status === 'UNPLAYABLE') {
    const errorScreen =
      playabilityStatus.error_screen as YTNodes.PlayerErrorMessage | null;
    throw new Error(
      `[${playabilityStatus.status}] ${errorScreen?.reason.text}: ${errorScreen?.subreason.text}`,
    );
  }

  const selectedPreset = config.selectedPreset ?? 'mp3 (256kbps)';
  let presetSetting: Preset;
  if (selectedPreset === 'Custom') {

View on GitHub (pinned to 1e2aac5706)

Solutions

  1. Update the downloader plugin (and its youtube-ext dependency) to a version with a working bypass
  2. Use cookies/auth if the plugin supports it so getInfo is authenticated
  3. Slow down batch downloads and avoid VPN/datacenter IPs to reduce bot-flagging
  4. For age-restricted content, sign in via a supported mechanism or skip the track

Example fix

// before
results.push(await downloader.downloadSong(url)); // one failure kills the batch

// after
for (const url of urls) {
  try { await downloader.downloadSong(url); }
  catch (e) {
    if (e.message.includes('LOGIN_REQUIRED')) skipped.push({url, reason: e.message});
    else throw e;
  }
}
Defensive patterns

Strategy: fallback

Try / catch

try { await downloadSong(url); } catch (e) {
  if (/\[LOGIN_REQUIRED\]/.test(e.message)) { skipped.push({ url, reason: e.message }); return; }
  throw e;
}

Prevention

When it happens

Trigger: Downloading age-restricted tracks where the TV-client bypass no longer works; YouTube flagging the IP/client as a bot ('Sign in to confirm you're not a bot'); region or account-gated content; the bypass endpoint being deprecated in the installed youtube-ext version.

Common situations: YouTube tightening bot/age verification (common waves of 'Sign in to confirm you're not a bot'); downloading from datacenter IPs or VPNs; outdated plugin versions whose Android TV bypass has been blocked; large batch downloads triggering rate-based sign-in walls.

Related errors


AI-assisted analysis of pear-devs/pear-desktop@1e2aac5706 (2026-08-27). Data as JSON: /api/errors/420a1695eaf812ae. Report an issue: GitHub.