langchain-ai/deepagents · error · RuntimeError

MCP token file {path} is not a JSON object (found {type(data

Error message

MCP token file {path} is not a JSON object (found {type(data).__name__}). Delete it and run `/mcp login {self._server_name}` in the TUI (or `dcode mcp login {self._server_name}`).

What it means

After JSON parsing succeeds, _read verifies the document is a JSON object; null, arrays, or bare scalars would otherwise cause AttributeError later. The file exists and parses but has the wrong shape, so it is treated as corrupt and a RuntimeError is raised with re-login instructions. The message deliberately prints only the type, never the value, because the file content is attacker-controlled.

Source

Thrown at libs/code/deepagents_code/mcp_auth.py:723

                f"Failed to read MCP token file {path}: {exc}. "
                f"Delete the file and run `/mcp login {self._server_name}` "
                f"in the TUI (or `dcode mcp login {self._server_name}`)."
            )
            raise RuntimeError(msg) from exc
        # `json.loads` yields a `dict` only for object literals; `null`, a list,
        # or a bare scalar would make the `.get` below raise `AttributeError`,
        # which callers do not catch. Fail as a normal corrupt-file error.
        if not isinstance(data, dict):
            msg = (
                f"MCP token file {path} is not a JSON object (found "
                f"{type(data).__name__}). Delete it and run "
                f"`/mcp login {self._server_name}` in the TUI (or "
                f"`dcode mcp login {self._server_name}`)."
            )
            # Not `TypeError` (TRY004): this is a corrupt-file report, not a
            # caller type error, and callers catch the same `RuntimeError` the
            # other corruption modes raise. `TypeError` would escape them.
            raise RuntimeError(msg)  # noqa: TRY004
        if data.get("version") != _STORAGE_VERSION:
            # Render only the value's type, never the value itself: callers
            # print this message verbatim (e.g. `mcp login` list on stderr),
            # and the version field is attacker-controlled file content that
            # could carry credential material planted by a malformed write.
            msg = (
                f"MCP token file {path} has unsupported version "
                f"({type(data.get('version')).__name__}; expected "
                f"{_STORAGE_VERSION!r}). Delete it and run "
                f"`/mcp login {self._server_name}` in the "
                f"TUI (or `dcode mcp login {self._server_name}`)."
            )
            raise RuntimeError(msg)
        return data

    def _write(self, data: dict) -> None:
        path = self.path
        path.parent.mkdir(parents=True, exist_ok=True)

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Delete the offending token file shown in the message.
  2. Re-authenticate via `/mcp login <server>` (TUI) or `dcode mcp login <server>`.
  3. If a tooling script writes this file, fix it to emit a JSON object with the expected schema/version fields.

Example fix

// before
$ cat github.json
null
// after
$ rm github.json && dcode mcp login github
Defensive patterns

Strategy: validation

Validate before calling

import json
from pathlib import Path
p = Path(token_store_dir()) / f"{server_name}.json"
if p.exists():
    data = json.loads(p.read_text(encoding="utf-8"))
    if not isinstance(data, dict):
        p.unlink()
        run_login(server_name)

Type guard

def is_token_dict(data: object) -> TypeGuard[dict]:
    return isinstance(data, dict)

Try / catch

try:
    tokens = store.get_tokens_sync()
except RuntimeError as e:
    if "is not a JSON object" in str(e):
        store.path.unlink(missing_ok=True)
        reauthenticate(server_name)
    else:
        raise

Prevention

When it happens

Trigger: Any of the _read-backed getters/setters encountering a token file whose top-level JSON value is not an object (e.g. `null`, `[]`, `"str"`, `123`).

Common situations: Someone ran `echo null > token.json` while debugging, a truncating editor emptied then saved `null`, or a script overwrote the file with a JSON array of tokens instead of the expected object schema.

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/9f1dceb607cd7000. Report an issue: GitHub.