datawhalechina/hello-agents · error · PatchApplyError

Disallowed file suffix for write: {target.suffix}

Error message

Disallowed file suffix for write: {target.suffix}

What it means

Raised by _enforce_suffix when the target file has a non-empty suffix that is not in the executor's allowed_write_suffixes allowlist. This confines writes to known-safe text file types and blocks accidental edits to binaries, lockfiles, secrets, and config files. Files with no suffix at all (Makefile, Dockerfile) pass through.

Source

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

        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: 当文件后缀不在允许列表中时抛出
        """
        if target.suffix and target.suffix not in self.allowed_write_suffixes:
            raise PatchApplyError(f"Disallowed file suffix for write: {target.suffix}")

    def _backup_file(self, target: Path, backup_run_dir: Path) -> Path:
        """
        备份目标文件到指定的备份目录。
        备份文件保持与原文件相同的相对路径结构,后缀添加 .bak。
        
        参数:
            target: 要备份的目标文件路径
            backup_run_dir: 本次运行的备份目录
            
        返回:
            Path: 创建的备份文件路径
        """
        # 获取文件相对于仓库根目录的路径
        rel = target.relative_to(self.repo_root)
        # 构建备份文件路径
        backup_path = backup_run_dir / (str(rel) + ".bak")
        # 创建备份文件的父目录(如果不存在)

View on GitHub (pinned to 606a07d341)

Solutions

  1. Change the patch to target an allowed text file, or split binary/asset edits out of the patch workflow.
  2. If the extension is genuinely safe to edit, add it to the allowed_write_suffixes list when constructing the executor.
  3. For extensionless files, confirm they pass (no suffix) rather than fighting the allowlist.

Example fix

# before
executor = ApplyPatchExecutor(repo_root=root)  # default allowlist
patch targets 'config/secrets.env' -> PatchApplyError

# after
executor = ApplyPatchExecutor(
    repo_root=root,
    allowed_write_suffixes={'.py', '.md', '.txt', '.yaml', '.json'},
)
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = executor.allowed_write_suffixes
for kind, path, _ in executor._parse_patch(patch_text):
    suffix = Path(path).suffix
    if suffix and suffix not in ALLOWED:
        raise ValueError(f'{path}: suffix {suffix} not in allowlist; edit an allowed file type')

Type guard

def suffix_allowed(path: str, allowed: set) -> bool:
    s = Path(path).suffix
    return (not s) or (s in allowed)

Try / catch

try:
    executor.apply(patch)
except PatchApplyError as e:
    if 'Disallowed file suffix' in str(e):
        drop_disallowed_files_from_patch_and_retry()
    raise

Prevention

When it happens

Trigger: Patching a .env, .bin, .pem, .lock, or any extension not in allowed_write_suffixes; creating a new file with a disallowed extension via '*** Add File: '.

Common situations: Agent tries to edit package-lock.json, .env, or an image; a project policy allowlist that omits a legitimate extension (e.g. .md, .yaml) the team needs; generated patches referencing asset files.

Related errors


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