pear-devs/pear-desktop · error · Error

[${playabilityStatus.status}] ${errorScreen?.reason.text}: $

Error message

[${playabilityStatus.status}] ${errorScreen?.reason.text}: ${errorScreen?.subreason.text}

What it means

When YouTube reports playability status UNPLAYABLE, the downloader throws an error embedding the status plus the PlayerErrorMessage reason/subreason from the error screen (e.g. '[UNPLAYABLE] Video unavailable: This video is not available in your country'). This is YouTube itself refusing to serve the track, not a plugin bug.

Source

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

  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') {
    presetSetting = config.customPresetSetting ?? DefaultPresetList['Custom'];
  } else if (selectedPreset === 'Source') {
    presetSetting = DefaultPresetList['Source'];
  } else {
    presetSetting = DefaultPresetList['mp3 (256kbps)'];
  }

  const downloadOptions: Types.FormatOptions = {
    type: (await isPremium()) ? 'audio' : 'video+audio', // Audio, video or video+audio
    quality: 'best', // Best, bestefficiency, 144p, 240p, 480p, 720p and so on.
    format: 'any', // Media container format

View on GitHub (pinned to 1e2aac5706)

Solutions

  1. Handle the error per-track in batch jobs (skip and log) instead of aborting the whole run
  2. For geo-blocks, route through an appropriate region or accept the content is unavailable
  3. Re-check the track in a browser to see the actual reason text; act accordingly (copyright = skip, region = different egress)
  4. Keep the plugin updated since error-screen parsing can drift with API changes

Example fix

// before
await downloader.downloadSongFromId(id);

// after
try {
  await downloader.downloadSongFromId(id);
} catch (e) {
  if (e.message.startsWith('[UNPLAYABLE]')) log.warn(`Skipped ${id}: ${e.message}`);
  else throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try { await downloadSongFromId(id); } catch (e) {
  if (/^\[UNPLAYABLE\]/.test(e.message)) { log.warn(`Skipped ${id}: ${e.message}`); return; }
  throw e;
}

Prevention

When it happens

Trigger: Region-locked tracks; videos removed for copyright or policy reasons; purchased/ premium-only content; embedding-restricted or otherwise gated media — any case where playabilityStatus.status is 'UNPLAYABLE' after info fetch.

Common situations: Geo-restriction when downloading from another country; tracks deleted between listing and downloading; copyright takedowns; batch-archiving playlists where some entries have gone dark.

Related errors


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