langchain-ai/deepagents · error · FleetImportError

{path}: unsafe subagent name {name!r}

Error message

{path}: unsafe subagent name {name!r}

What it means

A subagent directory name taken from `subagents/<name>/AGENTS.md` or `subagents/<name>/tools.json` fails validation: it must fully match `[A-Za-z0-9_.-]{1,128}` and not be `.` or `..`. Names outside this set could escape the `agents/` directory or produce unusable paths, so import aborts with the offending path in the message.

Source

Thrown at libs/talon/deepagents_talon/fleet_import.py:306


def _copy_zip_file(archive: zipfile.ZipFile, info: zipfile.ZipInfo, target: Path) -> None:
    target.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
    copied = 0
    with archive.open(info) as src, target.open("wb") as dst:
        while chunk := src.read(_COPY_CHUNK_SIZE):
            copied += len(chunk)
            if copied > info.file_size or copied > _MAX_ZIP_UNCOMPRESSED_BYTES:
                msg = f"{info.filename}: zip entry expanded beyond declared size"
                raise FleetImportError(msg)
            dst.write(chunk)
    target.chmod(0o600)


def _validate_agent_name(name: str, path: str) -> None:
    if not _AGENT_ID_PATTERN.fullmatch(name) or name in {".", ".."}:
        msg = f"{path}: unsafe subagent name {name!r}"
        raise FleetImportError(msg)


def _is_subagent_prompt_path(name: str) -> bool:
    parts = PurePosixPath(name).parts
    return (
        len(parts) == _SUBAGENT_FILE_PARTS and parts[0] == "subagents" and parts[2] == "AGENTS.md"
    )


def _is_subagent_tools_path(name: str) -> bool:
    parts = PurePosixPath(name).parts
    return (
        len(parts) == _SUBAGENT_FILE_PARTS and parts[0] == "subagents" and parts[2] == "tools.json"
    )


def _mcp_summaries(staging: Path, source: Path) -> list[_ServerSummary]:
    grouped: dict[tuple[str, str], _ServerSummary] = {}

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Rename the subagent directory in the export to only `[A-Za-z0-9_.-]`, max 128 chars (e.g. `my-assistant`)
  2. Pre-check names before zipping: regex-fullmatch every `subagents/*/` folder name
  3. Check the path in the error message to identify which folder to rename; update any cross-references to the old name

Example fix

// before
subagents/My Assistant!/AGENTS.md
// after
subagents/my-assistant/AGENTS.md
Defensive patterns

Strategy: validation

Validate before calling

import re
import zipfile

PATTERN = re.compile(r"[A-Za-z0-9_.-]{1,128}")

def has_unsafe_agent_names(path):
    with zipfile.ZipFile(path) as z:
        for name in z.namelist():
            parts = name.replace("\\", "/").split("/")
            if len(parts) == 3 and parts[0] == "subagents":
                if not PATTERN.fullmatch(parts[1]) or parts[1] in {".", ".."}:
                    return True
    return False

Type guard

def is_safe_agent_name(name: str) -> bool:
    return bool(re.fullmatch(r"[A-Za-z0-9_.-]{1,128}", name)) and name not in {".", ".."}

Try / catch

try:
    import_fleet_zip(zip_path, target_dir=target)
except FleetImportError as exc:
    if "unsafe subagent name" in str(exc):
        print(f"Rename subagent dir to [A-Za-z0-9_.-] only: {exc}")
    raise

Prevention

When it happens

Trigger: `import_fleet_zip` materializes a subagent file whose second path component contains characters like spaces, slashes, unicode, `~`, `!`, `@`, `#`, or is longer than 128 chars, or equals `.`/`..`.

Common situations: Fleet exports where subagent folders were named with human-friendly labels (`My Assistant!`), localization characters (`Assistanté`), or copy-paste artifacts (trailing spaces).

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/09750fd53c74c48f. Report an issue: GitHub.