langchain-ai/deepagents · error · FleetImportError

{source}: {_display_path(path)}: malformed tools.json: expec

Error message

{source}: {_display_path(path)}: malformed tools.json: expected object

What it means

The `tools.json` parsed as valid JSON but its top level is not a JSON object (e.g. an array, string, or number). The importer requires `dict` so it can read `tools` and `interrupt_config` keys, and aborts with this message otherwise.

Source

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

        for child in sorted(agents.iterdir(), key=lambda item: item.name):
            path = child / "tools.json"
            if path.is_file():
                paths.append((path, child.name))
    return paths


def _load_tool_requests(path: Path, scope: str, source: Path) -> list[_ToolRequest]:
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError as exc:
        msg = f"{source}: {_display_path(path)}: malformed tools.json: {exc.msg}"
        raise FleetImportError(msg) from exc
    except OSError as exc:
        msg = f"{source}: {_display_path(path)}: {exc}"
        raise FleetImportError(msg) from exc
    if not isinstance(data, dict):
        msg = f"{source}: {_display_path(path)}: malformed tools.json: expected object"
        raise FleetImportError(msg)

    raw_tools = data.get("tools")
    if not isinstance(raw_tools, list):
        msg = f"{source}: {_display_path(path)}: malformed tools.json: expected tools list"
        raise FleetImportError(msg)
    interrupt_config = data.get("interrupt_config")
    interrupts = interrupt_config if isinstance(interrupt_config, dict) else {}

    requests: list[_ToolRequest] = []
    for index, item in enumerate(raw_tools):
        if not isinstance(item, dict):
            msg = f"{source}: {_display_path(path)}: tools[{index}] must be an object"
            raise FleetImportError(msg)
        tool = cast("Mapping[str, object]", item)
        name = _required_str(tool, "name", path, index, source)
        server_url = _sanitize_server_url(
            _required_str(tool, "mcp_server_url", path, index, source)
        )

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Wrap the content: change top-level `[...]` to `{"tools": [...]}`
  2. Add the required keys: `{"tools": [{"name": ..., "mcp_server_url": ..., "mcp_server_name": ...}]}`
  3. Diff against a known-good tools.json from a working Fleet export to confirm the schema

Example fix

// before
[{"name": "search", "mcp_server_url": "https://mcp.example.com", "mcp_server_name": "web"}]
// after
{"tools": [{"name": "search", "mcp_server_url": "https://mcp.example.com", "mcp_server_name": "web"}]}
Defensive patterns

Strategy: type-guard

Validate before calling

import json
from typing import Any

def validate_tools_schema(path):
    data: Any = json.loads(open(path, encoding="utf-8").read())
    if not isinstance(data, dict) or not isinstance(data.get("tools"), list):
        raise ValueError(f"{path}: must be an object with a 'tools' list")

Type guard

from typing import Any

def is_tools_config(data: Any) -> bool:
    return isinstance(data, dict) and isinstance(data.get("tools"), list)

Try / catch

try:
    import_fleet_zip(zip_path, target_dir=target)
except FleetImportError as exc:
    if "malformed tools.json: expected object" in str(exc):
        print('Wrap content as {"tools": [...]}')
    raise

Prevention

When it happens

Trigger: `import_fleet_zip` → `_load_tool_requests` finds `json.loads(...) != dict`, e.g. a tools.json containing `[...]` or `"..."` at top level.

Common situations: Export tooling writing a bare list of tools instead of the expected `{"tools": [...]}` wrapper; hand-edits that replaced the object with an array; wrong file exported (config.json renamed tools.json).

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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