Repository navigation
[Bug]: specify extension add --dev shows a traceback when .extensionignore is a directory #5094
Description
Bug Description
load_install_ignore (src/specify_cli/_install_ignore.py:26-32) checks ignore_file.exists(), which is also true for a directory, then calls read_text, catching only UnicodeDecodeError. When an extension or preset source contains a directory named .extensionignore or .presetignore, IsADirectoryError escapes and the CLI prints a full traceback instead of a validation error.
Steps to Reproduce
From a fresh clone after uv sync, save this as repro.py and run uv run python -I repro.py from the clone root (it copies the bundled extensions/bug extension):
import shutil, subprocess, sys, tempfile
from pathlib import Path
specify = str(Path(sys.executable).parent / "specify")
tmp = Path(tempfile.mkdtemp())
proj = tmp / "p"
subprocess.run([specify, "init", str(proj), "--integration", "claude",
"--non-interactive", "--ignore-agent-tools"], capture_output=True)
# Any valid extension works; use the bundled "bug" extension from the clone.
ext = tmp / "ext"
shutil.copytree(Path("extensions/bug").resolve(), ext)
(ext / ".extensionignore").mkdir() # a directory, not a file
r = subprocess.run([specify, "extension", "add", "--dev", str(ext)],
cwd=proj, capture_output=True, text=True)
print("exit", r.returncode)
print("\n".join((r.stdout + r.stderr).strip().splitlines()[-3:]))Expected Behavior
A validation error naming the file, before anything is copied. The docstring at src/specify_cli/_install_ignore.py:20-21 says invalid ignore files "raise the caller's domain validation error before copying".
Actual Behavior
The command exits 1 after a Rich traceback that ends:
exit 1
╰──────────────────────────────────────────────────────────────────────────────╯
IsADirectoryError: [Errno 21] Is a directory:
'/tmp/tmpx54qa6n2/ext/.extensionignore'
Specify CLI Version
1.2.4.dev0 (main at 27d14e2)
AI Agent
Not applicable
Operating System
Linux 6.18 (Ubuntu container)
Python Version
Python 3.12.3
Error Logs
IsADirectoryError: [Errno 21] Is a directory:
'/tmp/tmpx54qa6n2/ext/.extensionignore'
Additional Context
The same function serves .presetignore for presets (src/specify_cli/presets/_manager.py:408). Checking is_file() and wrapping OSError in the caller's validation error would match the docstring.
Also reproduced on macOS (Darwin 25.6, arm64) with Python 3.14.4 and uv run python -I repro.py against 27d14e2; the output matches the above.
AI Disclosure
Found and reproduced with Claude Code (model: Claude Opus 4.8), run autonomously under human supervision. The human reporter re-ran every reproduction above before filing.
Activity