jackwener/OpenCLI · error · CliError
NOT_FOUND
NOT_FOUND
Error message
Episode not found
What it means
Thrown by the Xiaoyuzhou download command (clis/xiaoyuzhou/download.js:30) as a CliError with code NOT_FOUND when the API call to /v1/episode/get succeeds but response.data is empty/null. The library interprets this as 'no episode exists for the given eid'. It includes the hint 'Please check the ID' because the overwhelmingly common cause is a wrong or mistyped episode ID.
Source
Thrown at clis/xiaoyuzhou/download.js:30
access: 'read',
description: 'Download Xiaoyuzhou episode audio',
domain: 'www.xiaoyuzhoufm.com',
strategy: Strategy.LOCAL,
browser: false,
args: [
{ name: 'id', positional: true, required: true, help: 'Episode ID (eid from podcast-episodes output)' },
{ name: 'output', default: './xiaoyuzhou-downloads', help: 'Output directory' },
],
columns: ['title', 'podcast', 'status', 'size', 'file'],
func: async (args) => {
const credentials = loadXiaoyuzhouCredentials();
const response = await requestXiaoyuzhouJson('/v1/episode/get', {
query: { eid: args.id },
credentials,
});
const ep = response.data;
if (!ep) {
throw new CliError('NOT_FOUND', 'Episode not found', 'Please check the ID');
}
const audioUrl = ep.media?.source?.url;
if (!audioUrl) {
throw new CliError('PARSE_ERROR', 'Audio URL not found in episode payload', 'Episode payload does not expose media.source.url');
}
const output = String(args.output || './xiaoyuzhou-downloads');
const ext = path.extname(new URL(audioUrl).pathname) || '.mp3';
const title = String(ep.title || 'episode');
const filename = `${args.id}_${sanitizeFilename(title, 80) || 'episode'}${ext}`;
const outputDir = path.join(output, String(args.id));
fs.mkdirSync(outputDir, { recursive: true });
const destPath = path.join(outputDir, filename);
const result = await httpDownload(audioUrl, destPath, {
timeout: 60000,
});
return [{
title,
podcast: ep.podcast?.title || '',View on GitHub (pinned to 49907e53dc)
Solutions
- Verify the episode ID against the episode's share URL (the path segment after /episode/ in xiaoyuzhoufm.com links)
- Re-authenticate / refresh credentials, then retry — an expired token can yield empty data
- Try the same ID via the episode info command to confirm whether the API returns data at all
- If the episode was deleted or is region-restricted, no ID variant will work — use a different episode
Example fix
// before
await cli.download({ id: '62d0f3ea9a1f2b1a3c4d5e6' });
// after
const id = '62d0f3ea9a1f2b1a3c4d5e6f'; // full 24-char ID copied from episode URL
await cli.download({ id }); Defensive patterns
Strategy: validation
Validate before calling
function isValidEpisodeId(id) {
return typeof id === 'string' && /^[0-9a-f]{24}$/.test(id);
}
if (!isValidEpisodeId(args.id)) throw new Error('Episode ID must be 24 hex chars copied from the episode URL'); Type guard
function hasEpisodeData(res) {
return res != null && typeof res === 'object' && res.data != null && typeof res.data === 'object';
} Try / catch
try {
await cli.download({ id });
} catch (error) {
if (error.code === 'NOT_FOUND') {
console.error(`Episode ${id} does not exist — verify the ID from the episode share URL`);
} else throw error;
} Prevention
- Always copy the full 24-char episode ID from the episode share URL
- Distinguish episode IDs (eid) from podcast IDs (pid)
- Refresh auth tokens before long sessions
- Check the episode still exists in the app before scripting downloads
When it happens
Trigger: Running the xiaoyuzhou download command with an --id that the API does not resolve: nonexistent episode, truncated/mistyped ID, an episode that has been deleted or made private, or credentials (token) that lack access to the episode so the API returns empty data instead of 401.
Common situations: Copy-pasting an episode ID with missing characters; using an internal/preview ID not yet published; episode taken down by the podcaster; expired auth token causing the API to silently return empty data for restricted episodes.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- NOT_FOUND
- archive item returned malformed payload: files must be an ar
- archive search failed: HTTP ${resp.status}
- archive search returned malformed payload: response.docs mus
- No sidebar conversation matched "${raw}". Try the exact id f
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/afeb48c5ab7b17cb.
Report an issue: GitHub.