jackwener/OpenCLI · error · CommandExecutionError
Xiaoyuzhou playback progress returned an unexpected response
Error message
Xiaoyuzhou playback progress returned an unexpected response shape
What it means
parseProgressRows expects the playback-progress endpoint response to have `data` as an array of per-episode progress rows. If response.data is missing or not an array, the progress join cannot proceed, so CommandExecutionError is thrown for the /v1/playback-progress/list call made during `xiaoyuzhou history`.
Source
Thrown at clis/xiaoyuzhou/history.js:108
}
const episode = entry.episode;
if (!isRecord(episode.podcast) || typeof episode.isFinished !== 'boolean') {
throw new CommandExecutionError(`Xiaoyuzhou history row ${rowNumber} is missing podcast or finished state`);
}
return {
eid: requiredId(episode.eid, `eid in row ${rowNumber}`),
pid: requiredId(episode.pid, `pid in row ${rowNumber}`),
title: requiredString(episode.title, `title in row ${rowNumber}`),
podcast: requiredString(episode.podcast.title, `podcast title in row ${rowNumber}`),
durationSec: optionalSeconds(episode.duration, `duration in row ${rowNumber}`, { positive: true }),
pubDate: optionalIsoTime(episode.pubDate, `pubDate in row ${rowNumber}`, { required: true }),
finished: episode.isFinished,
};
}
function parseProgressRows(response, episodes) {
if (!Array.isArray(response?.data)) {
throw new CommandExecutionError('Xiaoyuzhou playback progress returned an unexpected response shape');
}
const requested = new Map(episodes.map((episode) => [episode.eid, episode]));
const progressById = new Map();
for (const [index, row] of response.data.entries()) {
if (!isRecord(row)) {
throw new CommandExecutionError(`Xiaoyuzhou playback progress row ${index + 1} is malformed`);
}
const eid = requiredId(row.eid, `progress eid in row ${index + 1}`);
const episode = requested.get(eid);
if (!episode) {
throw new CommandExecutionError(`Xiaoyuzhou playback progress returned unrequested eid ${eid}`);
}
if (progressById.has(eid)) {
throw new CommandExecutionError(`Xiaoyuzhou playback progress returned duplicate eid ${eid}`);
}
const pid = requiredId(row.pid, `progress pid in row ${index + 1}`);
if (pid !== episode.pid) {
throw new CommandExecutionError(`Xiaoyuzhou playback progress pid did not match history eid ${eid}`);View on GitHub (pinned to 49907e53dc)
Solutions
- Re-authenticate and retry (auth expiry between the history and progress calls is the most common cause).
- Log the raw progress response body to see the actual shape returned.
- Update the CLI/library in case the progress endpoint changed its response wrapper.
- Retry after a pause — rate limiting can cause non-list error bodies.
- Fix any transport/middleware that might rewrite the response before parsing.
Example fix
// before
{ "code": 401, "message": "token expired" }
// after
{ "data": [ { "eid": "...", "pid": "...", "progress": 300, "playedAt": "2026-08-29T01:00:00Z" } ] } Defensive patterns
Strategy: try-catch
Validate before calling
function hasProgressList(res) { return res !== null && typeof res === 'object' && Array.isArray(res.data); } Type guard
function isProgressResponse(v) { return typeof v === 'object' && v !== null && Array.isArray(v.data); } Try / catch
try {
const rows = await runHistory();
} catch (e) {
if (e.message === 'Xiaoyuzhou playback progress returned an unexpected response shape') {
// refresh credentials and retry once; likely auth expiry or an error body
} else throw e;
} Prevention
- Refresh credentials before long history runs so auth survives both API calls
- Watch for rate limits; space out large --all fetches
- Keep the CLI updated for progress-endpoint wrapper changes
When it happens
Trigger: The progress endpoint returns an error object, null data, or a wrapped object (e.g. { data: { list: [...] } }) instead of a top-level array, right after the history rows for a page were fetched.
Common situations: Auth expiry between the two calls returns an error body; API version change wraps the progress list differently; rate limiting returns a JSON error; proxy interference.
Related errors
- Xiaoyuzhou history returned an unexpected response shape
- Bilibili ${label} API returned a malformed payload
- Bilibili ${label} API failed: ${message} (${payload.code})
- Boss recommend response did not include zpData.friendList
- Boss geek chat list response did not include zpData.friendLi
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/f3cec2ece95bcf7e.
Report an issue: GitHub.