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
- Wrap the content: change top-level `[...]` to `{"tools": [...]}`
- Add the required keys: `{"tools": [{"name": ..., "mcp_server_url": ..., "mcp_server_name": ...}]}`
- 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
- Always emit tools.json as an object with a `tools` key
- Validate schema (dict + tools list) in your export pipeline
- Keep a canonical example tools.json and diff against it
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- {source}: {_display_path(path)}: malformed tools.json: {exc.
- Invalid MCP config at {mcp_config_path}: {exc}
- hooks trust store projects must be an object: {path}
- hooks trust store must be a JSON object: {path}
- MCP token file {path} is not a JSON object (found {type(data
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/8e49d390f74d4c7d.
Report an issue: GitHub.