{"record":{"id":"cf21679691c7ee5f","repo":"github/spec-kit","slug":"failed-to-save-preset-archive-e","errorCode":null,"errorMessage":"Failed to save preset archive: {e}","messagePattern":"Failed to save preset archive: (.+?)","errorType":"exception","errorClass":"PresetError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/presets/__init__.py","lineNumber":4959,"sourceCode":"            )\n            archive_path = build_safe_download_path(\n                target_dir,\n                pack_id,\n                version,\n                error_type=PresetError,\n                label=\"preset\",\n                suffix=archive_suffix(archive_format),\n            )\n            os.replace(staging_path, archive_path)\n            staging_path = None\n            return archive_path\n\n        except urllib.error.URLError as e:\n            raise PresetError(\n                f\"Failed to download preset from {download_url}: {e}\"\n            )\n        except IOError as e:\n            raise PresetError(f\"Failed to save preset archive: {e}\")\n        finally:\n            if staging_path is not None:\n                staging_path.unlink(missing_ok=True)\n\n    def clear_cache(self):\n        \"\"\"Clear all catalog cache files, including per-URL hashed caches.\"\"\"\n        if self.cache_dir.exists():\n            for f in self.cache_dir.iterdir():\n                if f.is_file() and f.name.startswith(\"catalog\"):\n                    f.unlink(missing_ok=True)\n\n\nclass PresetResolver:\n    \"\"\"Resolves template names to file paths using a priority stack.\n\n    Resolution order:\n    1. .specify/templates/overrides/          - Project-local overrides\n    2. .specify/presets/<preset-id>/          - Installed presets","sourceCodeStart":4941,"sourceCodeEnd":4977,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/presets/__init__.py#L4941-L4977","documentation":"The archive bytes downloaded but could not be persisted locally: an IOError/OSError (excluding URLError, caught earlier) occurred while writing the staging file or during the atomic os.replace into the cache. Raised as PresetError; the finally block unlinks the partial staging file so no corrupt archive remains.","triggerScenarios":"Cache/download directory is read-only or out of disk space; permissions missing on the cache dir; target path on a filesystem that fails the rename (cross-device or immutable file).","commonSituations":"Running with a restricted HOME or XDG cache; CI container with a small tmpfs filling up; cache dir owned by root after a previous sudo run; antivirus/locking interfering with os.replace on some platforms.","solutions":["Check free disk space and expand/clean the cache partition","Fix ownership/permissions of the preset cache directory (often ~/.cache/specify or the configured cache_dir)","Pass a writable target_dir to download_preset_archive if the default cache path is restricted","Remove stale root-owned archive files blocking os.replace"],"exampleFix":"# before\narchive = manager.download_preset_archive(pack_id)  # default cache dir read-only\n\n# after\narchive = manager.download_preset_archive(pack_id, target_dir=\"/tmp/writable-downloads\")","handlingStrategy":"try-catch","validationCode":"import shutil\ntarget = pathlib.Path(target_dir or manager.cache_dir / \"downloads\")\nwritable = target.exists() and os.access(target, os.W_OK)\nfree_ok = shutil.disk_usage(target).free > 100 * 1024 * 1024  # 100MB headroom","typeGuard":null,"tryCatchPattern":"try:\n    archive = manager.download_preset_archive(pack_id)\nexcept PresetError as e:\n    if \"Failed to save preset archive\" in str(e):\n        archive = manager.download_preset_archive(pack_id, target_dir=tempfile.mkdtemp())\n    else:\n        raise","preventionTips":["Ensure the preset cache directory is writable by the running user before downloads","Keep free disk space headroom sized to your largest preset archives","Avoid mixing sudo/root runs that leave root-owned files in the cache dir"],"tags":["file-io","presets","disk-space","permissions"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}