{"record":{"id":"ff9ec06d2c2d5079","repo":"ungoogled-software/ungoogled-chromium","slug":"could-not-find-series-file-in-existing-destination","errorCode":null,"errorMessage":"Could not find series file in existing destination: {destination / 'series'}","messagePattern":"Could not find series file in existing destination: (.+?)","errorType":"exception","errorClass":"FileNotFoundError","httpStatus":null,"severity":"error","filePath":"utils/patches.py","lineNumber":169,"sourceCode":"    \"\"\"Copy files from source to destination with relative paths from path_iter\"\"\"\n    for path in path_iter:\n        (destination / path).parent.mkdir(parents=True, exist_ok=True)\n        shutil.copy2(str(source / path), str(destination / path))\n\n\ndef merge_patches(source_iter, destination, prepend=False):\n    \"\"\"\n    Merges GNU quilt-formatted patches directories from sources into destination\n\n    destination must not already exist, unless prepend is True. If prepend is True, then\n    the source patches will be prepended to the destination.\n    \"\"\"\n    series = []\n    known_paths = set()\n    if destination.exists():\n        if prepend:\n            if not (destination / 'series').exists():\n                raise FileNotFoundError(\n                    f\"Could not find series file in existing destination: {destination / 'series'}\")\n            known_paths.update(generate_patches_from_series(destination))\n        else:\n            raise FileExistsError(f'destination already exists: {destination}')\n    for source_dir in source_iter:\n        patch_paths = tuple(generate_patches_from_series(source_dir))\n        patch_intersection = known_paths.intersection(patch_paths)\n        if patch_intersection:\n            raise FileExistsError(f'Patches from {source_dir} have conflicting paths '\n                                  f'with other sources: {patch_intersection}')\n        series.extend(patch_paths)\n        _copy_files(patch_paths, source_dir, destination)\n    if prepend and (destination / 'series').exists():\n        series.extend(generate_patches_from_series(destination))\n    with (destination / 'series').open('w') as series_file:\n        series_file.write('\\n'.join(map(str, series)))\n\n","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/ungoogled-software/ungoogled-chromium/blob/f85e84a480e2e17c103de7013de94320f9a2ba39/utils/patches.py#L151-L187","documentation":"merge_patches raises FileNotFoundError when prepend=True and the destination directory exists but has no `series` file. Prepending requires an existing series file to read current patch order so new patches can be placed before it; without it the merge cannot proceed.","triggerScenarios":"Calling merge_patches (or merge_platform_patches) with prepend=True while `destination` exists as a directory but lacks a `series` file — e.g. an empty or partially-created destination directory.","commonSituations":"Destination created by a previous failed merge or `mkdir -p` without copying the series file; typo in destination path pointing at an existing but unrelated directory; interrupted first merge left an empty dir.","solutions":["Create the missing `destination/series` file (possibly empty) or populate it with the existing patch list","Remove the empty/stale destination directory so merge_patches can create it fresh","Pass prepend=False if you intended a fresh merge into a new directory","Verify destination points at a directory produced by a prior merge_patches run"],"exampleFix":"# before\nmerge_patches(sources, dest, prepend=True)  # dest exists but has no series\n# after\n(dest / 'series').touch()  # or delete stale dest\nmerge_patches(sources, dest, prepend=True)","handlingStrategy":"validation","validationCode":"from pathlib import Path\ndest = Path(destination)\nif dest.exists() and prepend and not (dest / 'series').exists():\n    (dest / 'series').touch()  # or abort: raise SystemExit(f'{dest}/series missing')","typeGuard":"def is_mergeable_destination(dest):\n    d = Path(dest)\n    return not d.exists() or (d / 'series').is_file()","tryCatchPattern":"try:\n    merge_patches(sources, dest, prepend=True)\nexcept FileNotFoundError as e:\n    logger.error('destination not usable for prepend: %s', e)\n    shutil.rmtree(dest)\n    merge_patches(sources, dest, prepend=True)","preventionTips":["Only prepend into destinations produced by a previous merge_patches run","Never mkdir the destination manually before merging","Recreate the destination from scratch after an aborted merge"],"tags":["file-not-found","patches","series-file","merge"],"backgroundTag":"missing-series-file","analyzedSha":"f85e84a480e2e17c103de7013de94320f9a2ba39","analyzedAt":"2026-08-29T10:07:17.606Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}