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 formatView on GitHub (pinned to 1e2aac5706)
Solutions
- Handle the error per-track in batch jobs (skip and log) instead of aborting the whole run
- For geo-blocks, route through an appropriate region or accept the content is unavailable
- Re-check the track in a browser to see the actual reason text; act accordingly (copyright = skip, region = different egress)
- 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
- Design batch downloads to skip-and-log per track
- Read the embedded reason text: region vs copyright vs removed need different handling
- Don't retry UNPLAYABLE tracks — the state is stable, not transient
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
- plugins.downloader.backend.feedback.video-id-not-found
- [${playabilityStatus.status}] ${playabilityStatus.reason}
AI-assisted analysis of pear-devs/pear-desktop@1e2aac5706 (2026-08-27).
Data as JSON: /api/errors/4aa084640e54a450.
Report an issue: GitHub.