{"record":{"id":"956ab63b86f532dc","repo":"jackwener/OpenCLI","slug":"error-956ab6","errorCode":null,"errorMessage":"该视频没有字幕（作者未开启 + 无自动字幕）。","messagePattern":"该视频没有字幕（作者未开启 \\+ 无自动字幕）。","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"warning","filePath":"clis/youtube/transcript.js","lineNumber":451,"sourceCode":"          language: track.languageCode,\n          kind: track.kind || 'manual',\n          available,\n          requestedLang: langPref || null,\n          langMatched: !!(langPref && track.languageCode === langPref),\n          langPrefixMatched: !!(langPref && track.languageCode !== langPref && track.languageCode.startsWith(langPref))\n        };\n      })()\n    `));\n        if (!segments && (!captionData || typeof captionData !== 'object' || Array.isArray(captionData))) {\n            throw new CommandExecutionError(`Failed to get caption info: ${typeof captionData === 'string' ? captionData : 'malformed response'}`);\n        }\n        if (captionData?.error) {\n            const msg = `${captionData.error}${captionData.available ? ' (available: ' + captionData.available.join(', ') + ')' : ''}`;\n            // \"No captions available\" 是合法 empty 数据条件（作者没开字幕 + YT 没自动生成），\n            // 与 bilibili subtitle 的 EmptyResultError 同模式。下游应按 code EMPTY_RESULT 跳过\n            // 重试和 softFail 计数。其它 error（HTTP / parse / 短暂空响应）仍按 fetch 失败抛。\n            if (captionData.error === 'No captions available for this video') {\n                throw new EmptyResultError('youtube transcript', '该视频没有字幕（作者未开启 + 无自动字幕）。');\n            }\n            throw new CommandExecutionError(msg);\n        }\n        if (!segments && typeof captionData?.captionUrl !== 'string') {\n            throw new CommandExecutionError('Malformed caption info payload');\n        }\n        // Warn if --lang was specified but not matched\n        if (captionData?.requestedLang && !captionData.langMatched && !captionData.langPrefixMatched) {\n            console.error(`Warning: --lang \"${captionData.requestedLang}\" not found. Using \"${captionData.language}\" instead. Available: ${captionData.available.join(', ')}`);\n        }\n        // Step 2: Fetch caption XML and parse segments\n        // Ensure caption URL requests srv3 XML format — YouTube may return empty\n        // responses when no explicit format is specified.\n        if (!segments) {\n            const originalCaptionUrl = captionData.captionUrl;\n            let captionUrl = originalCaptionUrl;\n            if (!/[&?]fmt=/.test(originalCaptionUrl)) {\n                captionUrl = originalCaptionUrl + (originalCaptionUrl.includes('?') ? '&' : '?') + 'fmt=srv3';","sourceCodeStart":433,"sourceCodeEnd":469,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/youtube/transcript.js#L433-L469","documentation":"When the page reports the video has no caption track at all ('No captions available for this video'), the CLI raises EmptyResultError instead of CommandExecutionError. Per the source comment, this is a legitimate empty-data condition (uploader disabled subtitles and YouTube generated none), so downstream logic treats it as code EMPTY_RESULT — skipping retries and softFail counting. The message is Chinese: 'this video has no subtitles (uploader did not enable + no auto captions)'.","triggerScenarios":"`segments` on a video where captionData.error === 'No captions available for this video' — the uploader turned off subtitles and YouTube produced no auto-generated track.","commonSituations":"Transcribing music videos, uploads with captions disabled, or region-locked videos with no auto-captions; processing bulk video lists where some items simply lack subtitles.","solutions":["Treat this as a normal empty outcome: catch EmptyResultError and skip the video without counting a failure.","Confirm in the YouTube UI that the video really has no CC button before reporting a bug.","For pipelines, filter such videos upstream or record them as 'no subtitles' rather than errors.","Consider an alternative source (e.g. ASR transcription) if subtitles are mandatory."],"exampleFix":"// before\nconst segments = await getSegments(videoId); // crashes pipeline\n// after\ntry {\n  const segments = await getSegments(videoId);\n} catch (err) {\n  if (err instanceof EmptyResultError) return { videoId, segments: [], empty: true };\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const segments = await getSegments(videoId);\n} catch (err) {\n  if (err instanceof EmptyResultError) {\n    return { videoId, segments: [], empty: true }; // skip: no captions exist\n  }\n  throw err;\n}","preventionTips":["Treat EmptyResultError as a normal outcome, not a failure","Pre-check videos in bulk pipelines for a CC button via the UI/player API","Configure downstream to skip EMPTY_RESULT codes without retries or softFail counts","Provide an ASR fallback if subtitles are mandatory for your workflow"],"tags":["youtube","empty-result","no-captions","expected-condition"],"backgroundTag":"empty-result","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}