affaan-m/ECC · error · ValueError

refusing to remove {project_dir}: escapes {projects_root}

Error message

refusing to remove {project_dir}: escapes {projects_root}

What it means

Error "refusing to remove {project_dir}: escapes {projects_root}" thrown in affaan-m/ECC.

Source

Thrown at skills/continuous-learning-v2/scripts/instinct-cli.py:628

            observations_count = 0

    return {
        "personal": personal_count,
        "inherited": inherited_count,
        "observations": observations_count,
        "total": personal_count + inherited_count + observations_count,
    }


def _remove_project_storage(project_id: str) -> None:
    # Defense-in-depth: resolve and confirm the target is contained within
    # PROJECTS_DIR before recursively deleting, even though callers validate the
    # project id. A relaxed validator or a future caller must never be able to
    # turn this into an arbitrary-directory delete.
    projects_root = PROJECTS_DIR.resolve()
    project_dir = (PROJECTS_DIR / project_id).resolve()
    if project_dir == projects_root or projects_root not in project_dir.parents:
        raise ValueError(f"refusing to remove {project_dir}: escapes {projects_root}")
    if project_dir.exists():
        shutil.rmtree(project_dir)


def _project_instinct_ids(project_dir: Path, source_type: str) -> set[str]:
    instinct_dir = project_dir / "instincts" / source_type
    return {
        inst.get("id")
        for inst in _load_instincts_from_dir(instinct_dir, source_type, "project")
        if inst.get("id")
    }


def _merge_instinct_dir(from_dir: Path, into_dir: Path, existing_ids: set[str]) -> tuple[int, int]:
    moved = 0
    skipped = 0
    if not from_dir.exists():
        return moved, skipped

View on GitHub (pinned to 01e15490f0)

Solutions

  1. The resolved project directory escapes the projects root, so deletion is refused. Fix the project_id so it names a child of PROJECTS_DIR (no '..', no symlink tricks).
  2. If the projects root itself was moved or symlinked, restore PROJECTS_DIR to a real directory and retry.
  3. Remove the directory manually only after confirming it is not shared with other data.

Example fix

# ensure project_id matches ^[A-Za-z0-9._-]+$ and PROJECTS_DIR is a real dir:
rm -rf -- "$PROJECTS_DIR/$safe_project_id"

When it happens

Trigger: Thrown at skills/continuous-learning-v2/scripts/instinct-cli.py:628 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/4cb55b0533afc006. Report an issue: GitHub.