{"record":{"id":"902baedd12b840ec","repo":"ytdl-org/youtube-dl","slug":"s-said-s-902bae","errorCode":null,"errorMessage":"%s said: %s","messagePattern":"%s said: %s","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/filmon.py","lineNumber":48,"sourceCode":"        'info_dict': {\n            'id': '2825',\n            'title': 'Popeye Series 1',\n            'description': 'The original series of Popeye.',\n        },\n        'playlist_mincount': 8,\n    }]\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n\n        try:\n            response = self._download_json(\n                'https://www.filmon.com/api/vod/movie?id=%s' % video_id,\n                video_id)['response']\n        except ExtractorError as e:\n            if isinstance(e.cause, compat_HTTPError):\n                errmsg = self._parse_json(e.cause.read().decode(), video_id)['reason']\n                raise ExtractorError('%s said: %s' % (self.IE_NAME, errmsg), expected=True)\n            raise\n\n        title = response['title']\n        description = strip_or_none(response.get('description'))\n\n        if response.get('type_id') == 1:\n            entries = [self.url_result('filmon:' + episode_id) for episode_id in response.get('episodes', [])]\n            return self.playlist_result(entries, video_id, title, description)\n\n        QUALITY = qualities(('low', 'high'))\n        formats = []\n        for format_id, stream in response.get('streams', {}).items():\n            stream_url = stream.get('url')\n            if not stream_url:\n                continue\n            formats.append({\n                'format_id': format_id,\n                'url': stream_url,","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/filmon.py#L30-L66","documentation":"Raised by the FilmOn VOD extractor when the API request to https://www.filmon.com/api/vod/movie?id=<id> fails with an HTTP error and the error body contains a 'reason' field. The extractor re-raises the server's own reason prefixed with the extractor name. It is marked expected=True, so it represents a server-side rejection (bad ID, removed movie, region block) rather than a parsing bug.","triggerScenarios":"_download_json raises ExtractorError whose cause is a compat_HTTPError; the code reads the response body and parses 'reason' from the JSON. Produced by requesting a nonexistent or delisted movie id, a VOD item not licensed for your region, or transient FilmOn API failures.","commonSituations":"Following outdated filmon: movie links; scraping channel guides whose movie entries expired; hitting the API from a region where FilmOn has no VOD rights.","solutions":["Open https://www.filmon.com/vod/view/movie-<id> in a browser to confirm the title is still listed and playable in your region","Correct the movie id if it was mistyped or truncated from the URL","Use a region-appropriate exit IP (FilmOn VOD rights vary by country)","Retry later if the reason text indicates a temporary API problem"],"exampleFix":"# before\nyoutube_dl 'https://www.filmon.com/vod/view/movie-999999'\n# ERROR: FilmOn said: <reason>\n\n# after\nyoutube_dl 'https://www.filmon.com/vod/view/movie-12345'  # valid, in-catalog id","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    info = ydl.extract_info('filmon:movie-%s' % movie_id)\nexcept ExtractorError as e:\n    if str(e).startswith('FilmOn said:'):\n        drop_movie_from_catalog(movie_id)  # server rejected; do not retry blindly","preventionTips":["Validate movie ids against the FilmOn catalog page before extraction","Cache 'FilmOn said:' failures so crawlers do not re-hit dead ids"],"tags":["filmon","extractor","api-error","geo-restriction"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}