{"record":{"id":"483cf28d61eb8bc5","repo":"yt-dlp/yt-dlp","slug":"this-stream-has-expired","errorCode":null,"errorMessage":"This stream has expired","messagePattern":"This stream has expired","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"yt_dlp/extractor/kukululive.py","lineNumber":88,"sourceCode":"                'ext': 'mp4',\n                'vcodec': vcodec,\n                'quality': quality_priority,\n            })\n        if traverse_obj(quality_meta, ('hlsaddr_audioonly', 0, {url_or_none})):\n            formats.append({\n                'format_id': join_nonempty(quality, 'audioonly'),\n                'url': quality_meta['hlsaddr_audioonly'][0],\n                'ext': 'm4a',\n                'vcodec': 'none',\n                'quality': quality_priority,\n            })\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n        html = self._download_webpage(url, video_id)\n\n        if '>タイムシフトが見つかりませんでした。<' in html:\n            raise ExtractorError('This stream has expired', expected=True)\n\n        title = clean_html(\n            get_element_by_id('livetitle', html.replace('<SPAN', '<span').replace('SPAN>', 'span>')))\n        description = self._html_search_meta('Description', html)\n        thumbnail = self._html_search_meta(['og:image', 'twitter:image'], html)\n\n        if self._search_regex(r'(var\\s+timeshift\\s*=\\s*false)', html, 'is livestream', default=False):\n            formats = []\n            for (desc, code) in [('high', 'Z'), ('low', 'ForceLow')]:\n                quality_meta = self._get_quality_meta(video_id, desc, code)\n                self._add_quality_formats(formats, quality_meta)\n                if desc == 'high' and traverse_obj(quality_meta, ('vcodec', 0)) == 'HEVC':\n                    self._add_quality_formats(\n                        formats, self._get_quality_meta(video_id, desc, code, force_h264='1'))\n\n            return {\n                'id': video_id,\n                'title': title,","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/yt-dlp/yt-dlp/blob/81ecd58b1394793e6da9998cc19fdb45657f1685/yt_dlp/extractor/kukululive.py#L70-L106","documentation":"Raised by the kukulu.live extractor when the stream page contains the Japanese marker 'タイムシフトが見つかりませんでした。' ('timeshift not found'). kukulu.live only archives live streams as 'timeshift' recordings for a retention period chosen by the broadcaster; once that window passes (or recording was never enabled), the URL serves an error page and extraction stops. It is raised with expected=True, so yt-dlp reports it as a clean, user-facing 'video unavailable' rather than a bug.","triggerScenarios":"Running extraction on a live.kukulu.live (/archive/) URL after _download_webpage() when the HTML contains '>タイムシフトが見つかりませんでした。<'. Happens when the timeshift retention elapsed, the broadcaster deleted the recording, or the stream was live-only and never recorded.","commonSituations":"Old stream links pasted in chats, blogs, or issue trackers months after the broadcast; broadcaster set short/no retention; stream removed by moderator; automated batch jobs processing historical URLs.","solutions":["Confirm in a browser that the URL really shows the 'timeshift not found' page; if the recording plays in a browser, run yt-dlp -U and retry","If the stream is currently live, use the live URL instead of the timeshift/archive URL","Find a re-upload or the broadcaster's own archive elsewhere; the recording is gone from kukulu's servers","For automation, treat this message as a permanent 'not available' signal and stop retrying the URL"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def is_kukulu_expired(err) -> bool:\n    from yt_dlp.utils import ExtractorError\n    return isinstance(err, ExtractorError) and 'This stream has expired' in str(err)","tryCatchPattern":"from yt_dlp.utils import ExtractorError\ntry:\n    info = ydl.extract_info(url, download=True)\nexcept ExtractorError as e:\n    if e.expected and 'This stream has expired' in str(e):\n        mark_permanently_unavailable(url)  # do not retry; recording is gone\n    else:\n        raise","preventionTips":["Extract kukulu timeshifts promptly after the broadcast; retention is set per-broadcaster and can be short","If the content matters, keep your own recording instead of relying on timeshift availability","In batch pipelines, cache known-expired URLs to avoid re-hitting them"],"tags":["livestream","timeshift","expired","kukulu","japanese","expected-error"],"backgroundTag":"stream-recording-expired","analyzedSha":"81ecd58b1394793e6da9998cc19fdb45657f1685","analyzedAt":"2026-08-22T12:21:25.439Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}