{"record":{"id":"658ee996ece546c2","repo":"ytdl-org/youtube-dl","slug":"s-returned-error-s-658ee9","errorCode":null,"errorMessage":"%s returned error: %s","messagePattern":"(.+?) returned error: (.+?)","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/playfm.py","lineNumber":47,"sourceCode":"            'upload_date': '20140722',\n            'uploader': 'Dan Drastic',\n            'uploader_id': '71170',\n            'view_count': int,\n            'comment_count': int,\n        },\n    }\n\n    def _real_extract(self, url):\n        mobj = re.match(self._VALID_URL, url)\n        video_id = mobj.group('id')\n        slug = mobj.group('slug')\n\n        recordings = self._download_json(\n            'http://v2api.play.fm/recordings/slug/%s' % slug, video_id)\n\n        error = recordings.get('error')\n        if isinstance(error, dict):\n            raise ExtractorError(\n                '%s returned error: %s' % (self.IE_NAME, error.get('message')),\n                expected=True)\n\n        audio_url = recordings['audio']\n        video_id = compat_str(recordings.get('id') or video_id)\n        title = recordings['title']\n        description = recordings.get('description')\n        duration = int_or_none(recordings.get('recordingDuration'))\n        timestamp = parse_iso8601(recordings.get('created_at'))\n        uploader = recordings.get('page', {}).get('title')\n        uploader_id = compat_str(recordings.get('page', {}).get('id'))\n        view_count = int_or_none(recordings.get('playCount'))\n        comment_count = int_or_none(recordings.get('commentCount'))\n        categories = [tag['name'] for tag in recordings.get('tags', []) if tag.get('name')]\n\n        return {\n            'id': video_id,\n            'url': audio_url,","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/playfm.py#L29-L65","documentation":"Raised by PlayFMIE when the v2api.play.fm recordings lookup returns a top-level 'error' dict. The message interpolates the extractor name and the API's own error.message. Expected=True — the API answered, but negatively (e.g. recording not found).","triggerScenarios":"GET http://v2api.play.fm/recordings/slug/<slug> returning {'error': {'message': ...}}: unknown/deleted slug, or the v2 API rejecting the request shape.","commonSituations":"play.fm shut down or restructured its API (the service has been unstable), making every slug error; deleted recordings; slug typos from hand-edited URLs.","solutions":["Check the slug resolves at play.fm in a browser","If all PlayFM URLs fail, the service/API is likely dead or changed — update yt-dlp and check extractor status","Search for the mix/recording under a different slug (title-based) on the site"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    info = ydl.extract_info(url)\nexcept ExtractorError as e:\n    if e.expected and 'PlayFM returned error' in str(e):\n        log.info('PlayFM API rejected slug %s', slug)","preventionTips":["Check the recording exists on play.fm before extraction","Assume API-wide PlayFM failures mean service instability, not your URL","Do not retry immediately — API errors here are not transient"],"tags":["playfm","api-error","dead-site","extractor-error","expected"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}