{"record":{"id":"39df4f5803ab82f0","repo":"commaai/openpilot","slug":"unknown-extension-ext","errorCode":null,"errorMessage":"unknown extension {ext}","messagePattern":"unknown extension (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"openpilot/tools/lib/logreader.py","lineNumber":99,"sourceCode":"    return self._enum\n\n  def __getattr__(self, name: str):\n    if name.startswith(\"__\") and name.endswith(\"__\"):\n      return getattr(self, name)\n    return getattr(self._evt, name)\n\n\nclass _LogFileReader:\n  def __init__(self, fn, only_union_types=False, sort_by_time=False, dat=None):\n    self.data_version = None\n    self._only_union_types = only_union_types\n\n    ext = None\n    if not dat:\n      _, ext = os.path.splitext(urllib.parse.urlparse(fn).path)\n      if ext not in ('', '.bz2', '.zst'):\n        # old rlogs weren't compressed\n        raise ValueError(f\"unknown extension {ext}\")\n\n      with FileReader(fn) as f:\n        dat = f.read()\n\n    if ext == \".bz2\" or dat.startswith(b'BZh9'):\n      dat = bz2.decompress(dat)\n    elif ext == \".zst\" or dat.startswith(b'\\x28\\xB5\\x2F\\xFD'):\n      # https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#zstandard-frames\n      dat = decompress_stream(dat)\n\n    ents = capnp_log.Event.read_multiple_bytes(dat)\n\n    self._ents = []\n    try:\n      for e in ents:\n        self._ents.append(CachedEventReader(e))\n    except capnp.KjException:\n      warnings.warn(\"Corrupted events detected\", RuntimeWarning, stacklevel=1)","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/tools/lib/logreader.py#L81-L117","documentation":"_LogFileReader derives the compression from the file extension (plus magic-byte fallback). Extensions other than '', '.bz2', '.zst' — e.g. '.gz', '.zip', '.capnp', or a stray suffix — raise ValueError('unknown extension'). This guards against silently misparsing non-log files.","triggerScenarios":"Calling LogReader('route/rlog.bz2.x') or a path ending in '.gz'/'.zip'/anything unexpected; also URLs whose path has a query-style suffix that urlparse keeps in the extension.","commonSituations":"Manually recompressed logs (gzip) instead of zstd/bzip2; passing an index file (.py/.json) to LogReader; rename scripts appending suffixes like '.1' or '.bak'.","solutions":["Use plain, '.bz2', or '.zst' rlog/qlog files — re-compress with `zstd` if needed","Strip any accidental suffix so the extension is exactly .bz2/.zst or none","To read in-memory data instead, pass the dat= bytes argument, which bypasses extension checking"],"exampleFix":"# before\nlogs = LogReader('segment/rlog.gz')\n\n# after\nimport gzip, subprocess\nraw = gzip.open('segment/rlog.gz').read()\nlogs = LogReader(None, dat=subprocess.run(['zstd'], input=raw, capture_output=True).stdout)","handlingStrategy":"validation","validationCode":"import os, urllib.parse\n\ndef valid_log_path(fn: str) -> bool:\n    ext = os.path.splitext(urllib.parse.urlparse(fn).path)[1]\n    return ext in ('', '.bz2', '.zst')","typeGuard":null,"tryCatchPattern":"try:\n    logs = LogReader(fn)\nexcept ValueError as e:\n    if 'unknown extension' in str(e):\n        recompress_to_zst(fn)  # then retry","preventionTips":["Standardize on .zst (current) or .bz2 when archiving logs","Never gzip rlogs — this reader does not accept .gz","Pass dat=bytes to bypass extension checks for in-memory data"],"tags":["logs","compression","validation","logreader"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}