jackwener/OpenCLI · error · CommandExecutionError
Xiaoyuzhou history row ${rowNumber} is missing podcast or fi
Error message
Xiaoyuzhou history row ${rowNumber} is missing podcast or finished state What it means
After confirming the row has an episode object, parseHistoryEpisode requires `episode.podcast` to be a record (its title is read later) and `episode.isFinished` to be a boolean. Missing either means the library cannot render the podcast column or the finished state, so it throws CommandExecutionError.
Source
Thrown at clis/xiaoyuzhou/history.js:93
throw new CommandExecutionError('Xiaoyuzhou history returned an unexpected response shape');
}
if (next == null || next === '') return { entries, next: null };
if (typeof next !== 'string' || !next.trim()) {
throw new CommandExecutionError('Xiaoyuzhou history returned an invalid loadMoreKey');
}
if (entries.length === 0) {
throw new CommandExecutionError('Xiaoyuzhou history returned an empty page with a continuation cursor');
}
return { entries, next: next.trim() };
}
function parseHistoryEpisode(entry, rowNumber) {
if (!isRecord(entry) || !isRecord(entry.episode)) {
throw new CommandExecutionError(`Xiaoyuzhou history row ${rowNumber} is malformed; expected episode metadata`);
}
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();View on GitHub (pinned to 49907e53dc)
Solutions
- Retry — if the API intermittently returns partial episode objects, a fresh run may be complete.
- Update the CLI/library to match any API field rename or type change (e.g. isFinished becoming an int).
- Inspect the raw episode object to confirm whether podcast metadata or isFinished is absent vs. retyped.
- Check the specific episode/podcast in the app — unpublished podcasts cause missing metadata rows.
- Patch the parser (or report upstream) to coerce 0/1 isFinished or handle missing podcast titles if the API legitimately allows them.
Example fix
// before
{ "episode": { "eid": "...", "isFinished": 1 } }
// after
{ "episode": { "eid": "...", "podcast": { "title": "P" }, "isFinished": true } } Defensive patterns
Strategy: type-guard
Validate before calling
function hasPodcastAndFinished(ep) { return ep !== null && typeof ep === 'object' && ep.podcast !== null && typeof ep.podcast === 'object' && typeof ep.isFinished === 'boolean'; } Type guard
function isWellFormedEpisode(ep) { return typeof ep === 'object' && ep !== null && typeof ep.podcast === 'object' && ep.podcast !== null && typeof ep.isFinished === 'boolean'; } Try / catch
try {
const rows = await runHistory();
} catch (e) {
if (/missing podcast or finished state/.test(e.message)) {
// retry, or fall back to rows without finished-state filtering
} else throw e;
} Prevention
- Keep the CLI updated for field renames/type changes in the episode schema
- Avoid fetching history immediately after deleting podcasts/episodes from the account
- Validate fixtures against the current episode schema before replaying
When it happens
Trigger: A history row's episode object omits `podcast` (or it is null/non-object) or omits `isFinished` (or it is 0/1/'true' instead of a boolean), during `xiaoyuzhou history` parsing.
Common situations: Podcast unpublished/deleted so its metadata is dropped; API version change renames isFinished or switches it to an integer flag; partial responses from a flaky network layer; old fixtures.
Related errors
- Xiaoyuzhou history row ${rowNumber} is malformed; expected e
- Xiaoyuzhou playback progress row ${index + 1} is malformed
- Bilibili ${label} API returned a malformed payload
- Bilibili ${label} API returned malformed data
- Bilibili ${label} API did not return replies
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e22dc8d451349398.
Report an issue: GitHub.