aio-libs/aiohttp · error · ValueError

data and json parameters can not be used at the same time

Error message

data and json parameters can not be used at the same time

What it means

Raised in `_request` (client.py:515-518) when both `data` and `json` are non-None on the same call. aiohttp needs to choose one body encoding strategy — `json=` wraps the value in `JsonPayload` (or `JsonBytesPayload`), while `data=` is for form/stream/raw bodies — and the two are mutually exclusive. The guard short-circuits before any payload is built.

Source

Thrown at aiohttp/client.py:516

        # NOTE: timeout clamps existing connect and read timeouts.  We cannot
        # set the default to None because we need to detect if the user wants
        # to use the existing timeouts by setting timeout to None.

        if self.closed:
            raise RuntimeError("Session is closed")

        method = method.upper()

        if ssl is sentinel:
            ssl = self._default_ssl
        if not isinstance(ssl, SSL_ALLOWED_TYPES):
            raise TypeError(
                "ssl should be SSLContext, Fingerprint, or bool, "
                f"got {ssl!r} instead."
            )

        if data is not None and json is not None:
            raise ValueError(
                "data and json parameters can not be used at the same time"
            )
        elif json is not None:
            if self._json_serialize_bytes is not None:
                data = payload.JsonBytesPayload(json, dumps=self._json_serialize_bytes)
            else:
                data = payload.JsonPayload(json, dumps=self._json_serialize)

        redirects = 0
        history: list[ClientResponse] = []
        version = self._version
        params = params or {}

        # Merge with default headers and transform to CIMultiDict
        headers = self._prepare_headers(headers)

        try:
            url = self._build_url(str_or_url)

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Pick one: pass the payload as `json=payload` for JSON, or `data=payload` for form/raw.
  2. In forwarding helpers, explicitly pop the unused one: `kwargs.pop('data', None) if 'json' in kwargs else None`.
  3. Use `aiohttp.payload.JsonPayload(obj)` explicitly if you need to send it via `data=` for unusual cases.

Example fix

// before
await session.post(url, data=form, json=payload)
// after
await session.post(url, json=payload)   # JSON body
// or
await session.post(url, data=form)      # form / raw body
Defensive patterns

Strategy: validation

Validate before calling

def build_kwargs(kwargs):
    if kwargs.get('data') is not None and kwargs.get('json') is not None:
        raise ValueError('pass either data= or json=, not both')
    return kwargs

Try / catch

try:
    await session.post(url, **kwargs)
except ValueError as e:
    if 'data and json' in str(e):
        kwargs.pop('data', None)  # keep json
        await session.post(url, **kwargs)
    else:
        raise

Prevention

When it happens

Trigger: Calling `session.post(url, data=some_dict, json=some_other_dict)`, or forwarding a request helper that always sets both kwargs (e.g., `**kwargs` merged with a default `json=`).

Common situations: Refactoring a helper that previously took `data=` to also support `json=` and forgetting to make them exclusive; merging per-call overrides onto default kwargs via dict update that leaves both populated; OpenAPI-generated clients populating both fields.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/cb6943a5f34d4233.json. Report an issue: GitHub.