{"record":{"id":"540b602e28680047","repo":"nexu-io/open-design","slug":"target-dir-already-contains-pet-files-pass-fo","errorCode":null,"errorMessage":"{target_dir} already contains pet files; pass --force to overwrite","messagePattern":"(.+?) already contains pet files; pass --force to overwrite","errorType":"exception","errorClass":"SystemExit","httpStatus":null,"severity":"warning","filePath":"skills/hatch-pet/scripts/package_custom_pet.py","lineNumber":89,"sourceCode":"        raise SystemExit(\"pet name is required\")\n    pet_id = slugify(raw_pet_name)\n    if not pet_id:\n        raise SystemExit(\"pet name must contain at least one letter or digit\")\n    display_name = (args.display_name or raw_pet_name).strip()\n\n    source = Path(args.spritesheet).expanduser().resolve()\n    source_format = validate_spritesheet(source)\n    target_dir = (\n        Path(args.output_dir).expanduser().resolve()\n        if args.output_dir\n        else Path(args.codex_home).expanduser().resolve() / \"pets\" / pet_id\n    )\n    target_dir.mkdir(parents=True, exist_ok=True)\n\n    target_sheet = target_dir / \"spritesheet.webp\"\n    manifest_path = target_dir / \"pet.json\"\n    if not args.force and (target_sheet.exists() or manifest_path.exists()):\n        raise SystemExit(f\"{target_dir} already contains pet files; pass --force to overwrite\")\n\n    write_webp_spritesheet(source, target_sheet, source_format)\n    manifest = {\n        \"id\": pet_id,\n        \"displayName\": display_name,\n        \"description\": args.description,\n        \"spritesheetPath\": target_sheet.name,\n    }\n    manifest_path.write_text(json.dumps(manifest, indent=2) + \"\\n\", encoding=\"utf-8\")\n\n    print(\n        json.dumps(\n            {\"ok\": True, \"pet_dir\": str(target_dir), \"manifest\": str(manifest_path)}, indent=2\n        )\n    )\n\n\nif __name__ == \"__main__\":","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/skills/hatch-pet/scripts/package_custom_pet.py#L71-L107","documentation":"Raised by package_custom_pet.py when the target pet directory already contains spritesheet.webp or pet.json and --force was not passed. This protects an existing installed pet from being silently overwritten.","triggerScenarios":"Re-packaging a pet into the same target_dir (default ${CODEX_HOME}/pets/<pet-id> or a custom --output-dir) where a previous package already wrote its files.","commonSituations":"Iterating on the same pet name without --force; reinstalling over an existing pet to update its spritesheet; default target resolving to a dir you already populated.","solutions":["Pass --force to overwrite the existing spritesheet.webp and pet.json.","Or pick a different --pet-name / --output-dir to keep both versions.","Confirm the existing pet.json is not one you want to preserve before using --force.","Re-run with the chosen option."],"exampleFix":"// before\npython package_custom_pet.py --pet-name Sprite --description \"...\" --spritesheet sheet.png\n# SystemExit: .../pets/sprite already contains pet files; pass --force to overwrite\n\n// after\npython package_custom_pet.py --pet-name Sprite --description \"...\" --spritesheet sheet.png --force","handlingStrategy":"validation","validationCode":"def target_is_clean(target_dir: Path, force: bool) -> bool:\n    if force:\n        return True\n    sheet = target_dir / \"spritesheet.webp\"\n    manifest = target_dir / \"pet.json\"\n    return not (sheet.exists() or manifest.exists())","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass --force when intentionally updating an existing pet.","Use distinct --pet-name / --output-dir for parallel iterations to avoid clobbering.","Back up the existing pet.json before repackaging with --force."],"tags":["cli","filesystem","overwrite-protection","system-exit"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}