{"record":{"id":"44d05f42959a8b4e","repo":"ytdl-org/youtube-dl","slug":"this-video-is-currently-unavailable-it-may-still","errorCode":null,"errorMessage":"This video is currently unavailable. It may still be uploading or processing.","messagePattern":"This video is currently unavailable\\. It may still be uploading or processing\\.","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/streamable.py","lineNumber":81,"sourceCode":"            return mobj.group('src')\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n\n        # Note: Using the ajax API, as the public Streamable API doesn't seem\n        # to return video info like the title properly sometimes, and doesn't\n        # include info like the video duration\n        video = self._download_json(\n            'https://ajax.streamable.com/videos/%s' % video_id, video_id)\n\n        # Format IDs:\n        # 0 The video is being uploaded\n        # 1 The video is being processed\n        # 2 The video has at least one file ready\n        # 3 The video is unavailable due to an error\n        status = video.get('status')\n        if status != 2:\n            raise ExtractorError(\n                'This video is currently unavailable. It may still be uploading or processing.',\n                expected=True)\n\n        title = video.get('reddit_title') or video['title']\n\n        formats = []\n        for key, info in video['files'].items():\n            if not info.get('url'):\n                continue\n            formats.append({\n                'format_id': key,\n                'url': self._proto_relative_url(info['url']),\n                'width': int_or_none(info.get('width')),\n                'height': int_or_none(info.get('height')),\n                'filesize': int_or_none(info.get('size')),\n                'fps': int_or_none(info.get('framerate')),\n                'vbr': float_or_none(info.get('bitrate'), 1000)\n            })","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/streamable.py#L63-L99","documentation":"Raised by StreamableIE when the AJAX endpoint ajax.streamable.com/videos/<id> returns a status code other than 2. Status 0 = still uploading, 1 = still processing, 3 = error/unavailable; only 2 (at least one file ready) proceeds. Marked expected=True because it is a normal transient or terminal site state.","triggerScenarios":"Extracting a streamable.com video immediately after upload (status 0/1), or extracting one whose encode failed or was removed (status 3).","commonSituations":"Automated pipelines that download a video seconds after it is created on streamable.com; clips deleted by the uploader or by moderation; clips expired from free accounts (Streamable deletes old free uploads).","solutions":["If the video was just uploaded, wait for transcoding to finish (typically seconds to a few minutes) and retry the extraction.","Check https://streamable.com/<id> in a browser: if the page says deleted/expired, the video is gone — re-upload or find a mirror.","For pipelines, poll with a bounded retry/backoff on this specific expected error rather than failing on the first attempt."],"exampleFix":"# before: single-shot extraction\ninfo = ydl.extract_info(url, download=True)\n# after: bounded retry while Streamable processes the upload\nimport time\nfor attempt in range(10):\n    try:\n        info = ydl.extract_info(url, download=True)\n        break\n    except ExtractorError as e:\n        if not (e.expected and 'unavailable' in str(e)):\n            raise\n        if attempt == 9:\n            raise\n        time.sleep(15)","handlingStrategy":"retry","validationCode":"# Check readiness via the same AJAX endpoint the extractor uses\nimport json, urllib.request\nv = json.load(urllib.request.urlopen('https://ajax.streamable.com/videos/%s' % vid))\nif v.get('status') != 2:\n    print('not ready: status=%s (0=uploading 1=processing 3=error)' % v.get('status'))","typeGuard":"def streamable_ready(v):\n    \"\"\"True when the Streamable video has at least one ready file.\"\"\"\n    return isinstance(v, dict) and v.get('status') == 2 and bool(v.get('files'))","tryCatchPattern":"for _ in range(MAX_TRIES):\n    try:\n        info = ydl.extract_info(url, download=True)\n        break\n    except ExtractorError as e:\n        if not (e.expected and 'currently unavailable' in str(e)):\n            raise\n        time.sleep(BACKOFF)\nelse:\n    raise RuntimeError('streamable never became ready: %s' % url)","preventionTips":["Poll status==2 before handing freshly uploaded Streamable URLs to the downloader.","Distinguish status 3 (permanent) from 0/1 (transient) and only retry the latter.","Free Streamable uploads expire — download soon after creation."],"tags":["transient","processing","retry","expected-error"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}