github/spec-kit · error · ValueError
Refusing to scaffold through symlinked path: {label}
Error message
Refusing to scaffold through symlinked path: {label} What it means
Raised while _assert_safe_scaffold_target walks each component of the target path relative to project_root: if any existing component (e.g. src, specify_cli, integrations, or the new package dir) is a symlink, scaffolding aborts. This blocks a symlink-based escape where writing 'inside' the tree actually lands in an attacker- or user-controlled location outside it. It mirrors the symlink-aware guarding used for integration manifests.
Source
Thrown at src/specify_cli/integration_scaffold.py:192
Walks each component of *target* under *project_root* and rejects any
existing symlinked directory (or symlinked target), then confirms the
write destination still resolves inside the repository root. Mirrors the
symlink-aware guarding used for integration manifests.
"""
try:
rel = target.relative_to(project_root)
except ValueError:
raise ValueError(
f"Refusing to scaffold outside the repository root: {target}"
) from None
current = project_root
for part in rel.parts:
current = current / part
if current.is_symlink():
label = current.relative_to(project_root).as_posix()
raise ValueError(f"Refusing to scaffold through symlinked path: {label}")
root_resolved = project_root.resolve()
try:
target.parent.resolve().relative_to(root_resolved)
except (OSError, ValueError):
raise ValueError(
f"Refusing to scaffold outside the repository root: {target}"
) from None
def scaffold_integration(
project_root: Path,
key: str,
integration_type: str,
) -> IntegrationScaffoldResult:
"""Create a minimal built-in integration package and test skeleton."""
clean_key = _clean_key(key)
normalized_type = integration_type.strip().lower()View on GitHub (pinned to bf88c9f9a8)
Solutions
- Replace the symlinked directory with a real directory (copy its contents in, remove the link): rm src/specify_cli/integrations && mv /somewhere/else integrations.
- If the link exists for workflow reasons, scaffold into a clean tree and copy the generated files over manually.
- Check with `find src tests -type l` from the repo root to locate the offending link named in the error message.
Example fix
# before: integrations is a symlink $ ls -l src/specify_cli/integrations -> ../../shared/integrations # after: real directory $ rm src/specify_cli/integrations && cp -r ../../shared/integrations src/specify_cli/integrations
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def has_symlink_on_path(project_root: Path, target: Path) -> bool:
rel = target.relative_to(project_root)
cur = project_root
for part in rel.parts:
cur = cur / part
if cur.is_symlink():
return True
return False Try / catch
try:
scaffold_integration(root, key, itype)
except ValueError as exc:
if "symlinked path" in str(exc):
print(f"remove the symlink named in: {exc}") # then fix manually
raise Prevention
- Periodically run `find . -type l` in the repo to catch stray symlinks.
- Avoid symlinking source directories between checkouts; copy or use git worktrees instead.
- Treat any symlink under src/ or tests/ as a defect in a scaffold target tree.
When it happens
Trigger: scaffold_integration target path traverses a directory that is a symlink, e.g. src/specify_cli/integrations -> /somewhere/else, or the target file itself already exists as a symlink; any component of rel.parts where current.is_symlink() is True.
Common situations: Developers symlinking the integrations directory from another checkout to share work; monorepo tooling that replaces source dirs with symlinks; a previously created package dir symlinked to a template dir.
Related errors
- Error: --short-name requires a value
- Error: --number must be a non-negative integer
- Error: branch_template must not place {slug} before {number}
- ERROR: --template requires a template name
- ERROR: SPECIFY_INIT_DIR does not point to an existing direct
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/28f2ce25fed0254a.
Report an issue: GitHub.