pear-devs/pear-desktop · error · Error
Expected an array, instead got ${typeof data}
Error message
Expected an array, instead got ${typeof data} What it means
After a successful first LRCLib search request, the provider asserts the JSON body is an array (the LRCLib search API returns a list of matches). If the body is an object, string, null, or the response is an HTML error page parsed as JSON into a non-array, it throws this TypeError-style error.
Source
Thrown at src/plugins/synced-lyrics/providers/LRCLib.ts:39
artist_name: artist,
track_name: title,
});
query.set('album_name', album!);
if (query.get('album_name') === 'undefined') {
query.delete('album_name');
}
let url = `${this.baseUrl}/api/search?${query.toString()}`;
let response = await fetch(url);
if (!response.ok) {
throw new Error(`bad HTTPStatus(${response.statusText})`);
}
let data = (await response.json()) as LRCLIBSearchResponse;
if (!data || !Array.isArray(data)) {
throw new Error(`Expected an array, instead got ${typeof data}`);
}
if (data.length === 0) {
if (!config()?.showLyricsEvenIfInexact) {
return null;
}
// Try to search with the alternative title (original language)
const trackName = alternativeTitle || title;
query = new URLSearchParams({ q: `${trackName}` });
url = `${this.baseUrl}/api/search?${query.toString()}`;
response = await fetch(url);
if (!response.ok) {
throw new Error(`bad HTTPStatus(${response.statusText})`);
}
data = (await response.json()) as LRCLIBSearchResponse;View on GitHub (pinned to 1e2aac5706)
Solutions
- Update the synced-lyrics plugin in case the API contract changed
- Fall back to another lyrics provider when this error occurs
- If behind a captive portal/proxy, fix connectivity first
- Cache prior results so schema failures don't repeat for the same track
Example fix
// before
const lyrics = await lrclib.search(info);
// after
try { return await lrclib.search(info); }
catch (e) {
if (!/Expected an array/.test(e.message)) throw e;
return await megalobiz.search(info);
} Defensive patterns
Strategy: fallback
Type guard
const isSearchArray = (d: unknown): d is LRCLIBSearchResponse => Array.isArray(d);
Try / catch
try { return await lrclib.search(info); } catch (e) { if (!/Expected an array/.test(e.message)) throw e; return await megalobiz.search(info); } Prevention
- Keep the synced-lyrics plugin updated for LRCLib API changes
- Detect captive portals (any HTTP fetch returning HTML) before lyric lookups
- Always have a second lyrics provider configured as fallback
When it happens
Trigger: LRCLib returning an error object instead of a list (e.g. {message: '...'} on bad parameters); an HTML page served with 200 (captive portal/proxy); API contract changes returning {results: [...]}; response.json() yielding null for an empty body.
Common situations: Captive portals and 'accepted connection' interstitials on public Wi-Fi; API version changes on lrclib.net; middleboxes injecting content; album_name query edge cases returning a structured error.
Related errors
- bad HTTPStatus(${response.statusText})
- Failed to extract lyrics from preloaded state.
- bad HTTPStatus(${response.statusText})
- Failed to extract lyrics from page.
AI-assisted analysis of pear-devs/pear-desktop@1e2aac5706 (2026-08-27).
Data as JSON: /api/errors/dbb1af06d909afc0.
Report an issue: GitHub.