datawhalechina/hello-agents · error · PatchApplyError

Absolute paths are not allowed: {rel_path}

Error message

Absolute paths are not allowed: {rel_path}

What it means

Raised by _safe_path when the rel_path argument begins with '/' or '~', i.e. the caller passed an absolute POSIX path or a home-relative path instead of a path relative to repo_root. The executor deliberately confines all writes to repo_root, so absolute targets are rejected before any filesystem access.

Source

Thrown at Co-creation-projects/YYHDBL-HelloCodeAgentCli/code_agent/executors/apply_patch_executor.py:200

        # 返回最终的应用结果
        return ApplyResult(files_changed=files_changed, backups=backups)

    def _safe_path(self, rel_path: str) -> Path:
        """
        验证路径安全性,防止路径遍历攻击 (Path Traversal)。
        确保目标路径在 repo_root 目录下,防止访问仓库外的文件。
        
        参数:
            rel_path: 相对路径字符串
            
        返回:
            Path: 安全的绝对路径对象
            
        异常:
            PatchApplyError: 当路径是绝对路径、包含特殊字符或试图访问仓库外时抛出
        """
        if rel_path.startswith("/") or rel_path.startswith("~"):
            raise PatchApplyError(f"Absolute paths are not allowed: {rel_path}")
        target = (self.repo_root / rel_path).resolve()
        # 检查解析后的路径是否以 repo_root 开头
        if not str(target).startswith(str(self.repo_root.resolve()) + os.sep) and target != self.repo_root.resolve():
            raise PatchApplyError(f"Path escapes repo_root: {rel_path}")
        if target.exists() and target.is_symlink():
            raise PatchApplyError(f"Refusing to modify symlink: {rel_path}")
        return target

    def _enforce_suffix(self, target: Path) -> None:
        """
        检查目标文件的后缀是否在允许的列表中。
        防止意外修改二进制文件、配置文件或其他敏感文件。
        
        参数:
            target: 目标文件路径对象
            
        异常:
            PatchApplyError: 当文件后缀不在允许列表中时抛出

View on GitHub (pinned to 606a07d341)

Solutions

  1. Strip the repo_root prefix (or re-relativize) before building the patch: rel = os.path.relpath(abs_path, repo_root).
  2. Fix the patch text so file headers use repo-relative paths like 'src/main.py'.
  3. If the intent really is to write outside the repo, instantiate the executor with a repo_root that contains the target, rather than bypassing the guard.

Example fix

# before
'*** Update File: /home/user/proj/src/main.py'

# after
from pathlib import Path
rel = Path(abs_path).relative_to(repo_root).as_posix()  # 'src/main.py'
# use '*** Update File: src/main.py' in the patch
Defensive patterns

Strategy: validation

Validate before calling

def to_rel(abs_or_rel: str, repo_root: Path) -> str:
    p = Path(abs_or_rel)
    if p.is_absolute() or abs_or_rel.startswith('~'):
        p = Path(os.path.expanduser(abs_or_rel))
        return p.resolve().relative_to(repo_root.resolve()).as_posix()
    return abs_or_rel

rel = to_rel(model_path, repo_root)
assert not rel.startswith(('/', '~')), 'must be repo-relative'

Type guard

def is_repo_relative(rel_path: str) -> bool:
    return not rel_path.startswith('/') and not rel_path.startswith('~')

Try / catch

try:
    executor.apply(patch)
except PatchApplyError as e:
    if 'Absolute paths are not allowed' in str(e):
        patch = rewrite_paths_relative(patch, repo_root)
        executor.apply(patch)
    else:
        raise

Prevention

When it happens

Trigger: Passing '/etc/passwd', '~/notes.md', or any path starting with '/' as the file path in an '*** Add File: ' / '*** Update File: ' section of the patch; LLM-generated patches that emit absolute paths copied from tool output.

Common situations: Agent models echoing absolute paths from a previous tool result; patches authored on a different machine with absolute paths; scripts reusing a resolved Path object where a relative string is expected.

Related errors


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/e35493fd2beb0d4b. Report an issue: GitHub.