{"record":{"id":"4292bd061c848cd8","repo":"github/spec-kit","slug":"preserved-extension-config-for-manifest-id-is","errorCode":null,"errorMessage":"Preserved extension config for '{manifest.id}' is a symlink ({cfg_file.name}) in {dest_dir}, which cannot be safely rescued during reinstall. Resolve manually — replace the symlink with a regular file or remove it — then reinstall.","messagePattern":"Preserved extension config for '(.+?)' is a symlink \\((.+?)\\) in (.+?), which cannot be safely rescued during reinstall\\. Resolve manually — replace the symlink with a regular file or remove it — then reinstall\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":2371,"sourceCode":"            and not self.registry.is_installed(manifest.id)\n            and (\n                self._has_keep_config_marker(dest_dir)\n                or self._is_legacy_keep_config_leftover(dest_dir)\n            )\n        ):\n            for cfg_file in (\n                list(dest_dir.glob(\"*-config.yml\"))\n                + list(dest_dir.glob(\"*-config.local.yml\"))\n            ):\n                if cfg_file.is_symlink():\n                    # `remove --keep-config` preserves a symlinked config\n                    # because Path.is_file() follows symlinks. Its bytes cannot\n                    # be safely rescued (the target may live outside dest_dir),\n                    # and the rmtree below would delete the link and silently\n                    # discard the kept configuration. Reject the reinstall while\n                    # dest_dir is untouched so the user resolves it rather than\n                    # losing the linked config.\n                    raise ValidationError(\n                        \"Preserved extension config for \"\n                        f\"'{manifest.id}' is a symlink ({cfg_file.name}) in \"\n                        f\"{dest_dir}, which cannot be safely rescued during \"\n                        \"reinstall. Resolve manually — replace the symlink with \"\n                        \"a regular file or remove it — then reinstall.\"\n                    )\n                if cfg_file.is_file():\n                    # A kept config that cannot be read or stat'ed must not\n                    # crash the reinstall with a raw OSError — and must not\n                    # reach the rmtree below unrescued. Like the symlink\n                    # guard above, reject while dest_dir is untouched so the\n                    # preserved bytes are never lost.\n                    try:\n                        stranded_configs[cfg_file.name] = (\n                            cfg_file.read_bytes(),\n                            cfg_file.stat().st_mode,\n                        )\n                    except OSError as exc:","sourceCodeStart":2353,"sourceCodeEnd":2389,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L2353-L2389","documentation":"Error \"Preserved extension config for '{manifest.id}' is a symlink ({cfg_file.name}) in {dest_dir}, which cannot be safely rescued during reinstall. Resolve manually — replace the symlink with a regular file or remove it — then reinstall.\" thrown in github/spec-kit.","triggerScenarios":"Thrown at src/specify_cli/extensions/__init__.py:2371 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Replace the symlinked config file with a regular file (or remove it), then reinstall the extension."],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}