{"record":{"id":"c3041e4df849616b","repo":"ytdl-org/youtube-dl","slug":"s-returned-error-s-c3041e","errorCode":null,"errorMessage":"%s returned error: %s","messagePattern":"(.+?) returned error: (.+?)","errorType":"exception","errorClass":"ExtractorError","httpStatus":400,"severity":"error","filePath":"youtube_dl/extractor/vidme.py","lineNumber":149,"sourceCode":"            'skip_download': True,\n        },\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://api.vid.me/videoByUrl/%s' % video_id, video_id)\n        except ExtractorError as e:\n            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 400:\n                response = self._parse_json(e.cause.read(), video_id)\n            else:\n                raise\n\n        error = response.get('error')\n        if error:\n            raise ExtractorError(\n                '%s returned error: %s' % (self.IE_NAME, error), expected=True)\n\n        video = response['video']\n\n        if video.get('state') == 'deleted':\n            raise ExtractorError(\n                'Vidme said: Sorry, this video has been deleted.',\n                expected=True)\n\n        if video.get('state') in ('user-disabled', 'suspended'):\n            raise ExtractorError(\n                'Vidme said: This video has been suspended either due to a copyright claim, '\n                'or for violating the terms of use.',\n                expected=True)\n\n        formats = []\n        for f in video.get('formats', []):\n            format_url = url_or_none(f.get('uri'))","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/vidme.py#L131-L167","documentation":"Raised by the Vidme extractor when the api.vid.me response JSON (normally fetched, or recovered from a 400 body) contains a non-empty top-level 'error' key. The API's message is passed through with the extractor name. expected=True — the API explicitly rejected the videoByUrl lookup.","triggerScenarios":"Extracting a vid.me URL where the API answers {\"error\": ...} — nonexistent video slug, API version changes, or Vidme's shutdown responses. The same handler also covers the branch where a 400 response body is manually parsed.","commonSituations":"Vid.me shut down in late 2017, so virtually every vid.me URL now errors; archived playlists still contain vid.me links; the API domain no longer serves the videoByUrl contract.","solutions":["Accept that Vidme is defunct — the API will not return video data for any id.","Remove/skip vid.me URLs from batch downloads and link checkers.","If you maintain a fork, consider deleting or disabling the extractor family since the service is dead.","Do not attempt retries; the error is a definitive API rejection."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import json, urllib.request\ntry:\n    resp = json.load(urllib.request.urlopen('https://api.vid.me/videoByUrl/%s' % video_id))\nexcept urllib.error.HTTPError as he:\n    resp = json.loads(he.read().decode())\nif resp.get('error'):\n    skip(video_id, 'vidme api: %s' % resp['error'])","typeGuard":null,"tryCatchPattern":"try:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if e.expected and 'returned error' in str(e):\n        archive_dead(url)  # service shut down; permanent\n    raise","preventionTips":["Strip vid.me URLs from crawl lists — the service is defunct.","Pre-check the API error field once and cache the verdict.","Classify service-shutdown domains as permanently dead in link checkers."],"tags":["api","service-shutdown","content-missing","site-side-rejection"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}