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
- Update the downloader plugin (and its youtube-ext dependency) to a version with a working bypass
- Use cookies/auth if the plugin supports it so getInfo is authenticated
- Slow down batch downloads and avoid VPN/datacenter IPs to reduce bot-flagging
- 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
- Keep the downloader plugin and youtube-ext updated — bypasses get patched frequently
- Authenticate via cookies if the plugin supports it
- Rate-limit batch downloads; avoid VPN/datacenter IPs to reduce bot-flagging
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
- plugins.downloader.backend.feedback.video-id-not-found
- [${playabilityStatus.status}] ${errorScreen?.reason.text}: $
AI-assisted analysis of pear-devs/pear-desktop@1e2aac5706 (2026-08-27).
Data as JSON: /api/errors/420a1695eaf812ae.
Report an issue: GitHub.