langchain-ai/deepagents · error · TypeError

Offload request must be a JSON object.

Error message

Offload request must be a JSON object.

What it means

The offload API expects the request payload to be a decoded JSON object (a Python dict at the top level). Anything else — a JSON array, string, number, or undecoded raw JSON text — is rejected with this TypeError before any field is inspected. This enforces the server's wire contract that an offload request is a single object with operation_id/context/hook_responses fields.

Source

Thrown at libs/code/deepagents_code/offload_api.py:516


def _operation_payload(
    payload: object,
) -> tuple[str, dict[str, Any], dict[str, object]]:
    """Validate the narrow client-to-operation request shape.

    Args:
        payload: Decoded request JSON.

    Returns:
        Operation id, runtime context, and accumulated hook responses.

    Raises:
        TypeError: If the payload or a structured field has the wrong shape.
    """
    if not isinstance(payload, dict):
        msg = "Offload request must be a JSON object."
        raise TypeError(msg)
    operation_id = payload.get("operation_id")
    context = payload.get("context")
    responses = payload.get("hook_responses", {})
    if not isinstance(operation_id, str) or not operation_id:
        msg = "operation_id must be a non-empty string."
        raise TypeError(msg)
    if not isinstance(context, dict):
        msg = "context must be a JSON object."
        raise TypeError(msg)
    if not isinstance(responses, dict):
        msg = "hook_responses must be a JSON object."
        raise TypeError(msg)
    validated_context = {str(key): value for key, value in context.items()}
    _validate_context(validated_context)
    return (
        operation_id,
        _strip_transport_model_params(validated_context),
        {str(key): value for key, value in responses.items()},

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Decode first: payload = json.loads(raw_body) before passing it on.
  2. If you get bytes, use json.loads(raw) (it accepts bytes) and confirm the result is a dict.
  3. If you intended to send multiple operations, send them one at a time or wrap them under an object key the API defines.
  4. Log/inspect type(payload) to confirm you are not passing a str wrapper around an already-parsed dict.

Example fix

// before
result = await offload('{"operation_id": "op1", "context": {}}')
// after
import json
result = await offload(json.loads('{"operation_id": "op1", "context": {}}'))
Defensive patterns

Strategy: type-guard

Validate before calling

import json

def ensure_object_payload(raw):
    payload = json.loads(raw) if isinstance(raw, (str, bytes, bytearray)) else raw
    if not isinstance(payload, dict):
        raise ValueError("Offload payload must decode to a JSON object")
    return payload

Type guard

def is_json_object(value) -> bool:
    return isinstance(value, dict)

Try / catch

try:
    state = await offload(thread_id, payload)
except TypeError as exc:
    if "must be a JSON object" in str(exc):
        state = await offload(thread_id, json.loads(payload))
    else:
        raise

Prevention

When it happens

Trigger: Calling offload() (or passing a payload into the request handling) with a JSON string like '{"operation_id": ...}' that was never json.loads()-ed, a list of operations, None, or a custom object instead of a dict.

Common situations: Double-encoded JSON from an HTTP client (body already a string), forwarding the raw request body without decoding, sending an array of requests where one object was expected, or a framework handing you a bytes body.

Related errors


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