datawhalechina/hello-agents · error · PatchApplyError

Disallowed file suffix for write

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.

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.

Appendix: 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)