{"record":{"id":"ba4f4ce9f96453e1","repo":"yt-dlp/yt-dlp","slug":"invalid-video-password","errorCode":null,"errorMessage":"Invalid video password","messagePattern":"Invalid video password","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"yt_dlp/extractor/loom.py","lineNumber":371,"sourceCode":"        return filter_dict({\n            'en': traverse_obj(subs_data, (\n                'data', 'fetchVideoTranscript',\n                ('source_url', 'captions_source_url'), {\n                    'url': {url_or_none},\n                })) or None,\n        })\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n        metadata = traverse_obj(\n            self._call_graphql_api('GetVideoSSR', video_id, 'Downloading GraphQL metadata JSON', fatal=False),\n            ('data', 'getVideo', {dict})) or {}\n\n        if metadata.get('__typename') == 'VideoPasswordMissingOrIncorrect':\n            if not self.get_param('videopassword'):\n                raise ExtractorError(\n                    'This video is password-protected, use the --video-password option', expected=True)\n            raise ExtractorError('Invalid video password', expected=True)\n\n        video_data = self._call_graphql_api(\n            'GetVideoSource', video_id, 'Downloading GraphQL video JSON')\n        chapter_data = self._call_graphql_api(\n            'FetchChapters', video_id, 'Downloading GraphQL chapters JSON', fatal=False)\n        duration = traverse_obj(metadata, ('video_properties', 'duration', {int_or_none}))\n\n        return {\n            'id': video_id,\n            'duration': duration,\n            'chapters': self._extract_chapters_from_description(\n                traverse_obj(chapter_data, ('data', 'fetchVideoChapters', 'content', {str})), duration) or None,\n            'formats': self._extract_formats(video_id, metadata, video_data),\n            'subtitles': self.extract_subtitles(video_id),\n            **traverse_obj(metadata, {\n                'title': ('name', {str}),\n                'description': ('description', {str}),\n                'uploader': ('owner', 'display_name', {str}),","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/yt-dlp/yt-dlp/blob/81ecd58b1394793e6da9998cc19fdb45657f1685/yt_dlp/extractor/loom.py#L353-L389","documentation":"Same Loom GraphQL signal (__typename 'VideoPasswordMissingOrIncorrect') but a video password WAS supplied via get_param('videopassword') — meaning the password was submitted and still rejected: it is wrong (changed, typo'd, or mangled by shell/API encoding). Raised with expected=True as 'Invalid video password'.","triggerScenarios":"Passing --video-password (or the videopassword option) whose value does not match the one the owner set on the Loom video; passwords with shell-special characters getting misquoted, or values accidentally URL-encoded twice when injected programmatically.","commonSituations":"Owner rotated the password; copy/paste with trailing whitespace; quoting bugs in scripts ($ or ! characters); password managers auto-filling a stale value.","solutions":["Re-confirm the current password with the video owner (passwords are case-sensitive)","Quote the value properly on the CLI: --video-password 'p@ss w0rd!'; in the Python API pass it as the videopassword option verbatim","Check scripts for double-encoding (percent-signs, URL-encoding) applied before the value reaches yt-dlp","Verify by opening the share URL in a browser and entering the same password"],"exampleFix":"# before (shell mangles the ! or spacing)\nVIDEO_PW=p@ss word! yt-dlp --video-password $VIDEO_PW <url>\n\n# after (single-quoted, exact value)\nyt-dlp --video-password 'p@ss word!' 'https://www.loom.com/share/abc123'","handlingStrategy":"retry","validationCode":null,"typeGuard":"def is_loom_bad_password(err) -> bool:\n    from yt_dlp.utils import ExtractorError\n    return isinstance(err, ExtractorError) and 'Invalid video password' in str(err)","tryCatchPattern":"import yt_dlp\nfrom yt_dlp.utils import ExtractorError\n\nfor pw in candidate_passwords:               # e.g. current + last-rotated value\n    try:\n        with yt_dlp.YoutubeDL({'videopassword': pw}) as ydl:\n            info = ydl.extract_info(url, download=True)\n        break\n    except ExtractorError as e:\n        if 'Invalid video password' not in str(e):\n            raise\nelse:\n    ask_owner_for_current_password()","preventionTips":["Single-quote --video-password values on the shell to avoid ! and $ expansion","Strip whitespace from programmatically sourced passwords before passing them","When owners rotate passwords, update your stored value at the same time"],"tags":["loom","wrong-password","videopassword","access-control","expected-error"],"backgroundTag":"invalid-media-password","analyzedSha":"81ecd58b1394793e6da9998cc19fdb45657f1685","analyzedAt":"2026-08-22T12:21:25.439Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}