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 by ClientSession.request when both data and json are not None. The two arguments imply mutually exclusive body encodings: data is form/stream-oriented, json is serialized with the configured serializer. Allowing both would leave the actual body undefined.

Solutions

  1. Pick one body form: json=obj for JSON APIs, data=... for forms or raw bodies.
  2. If you need JSON inside multipart/form fields, build a FormData and embed the JSON as one field instead of using the json kwarg.
  3. Audit wrapper functions to ensure only one of data/json is forwarded.

Example fix

// before
await session.post(url, data=form, json={'a': 1})
// after
await session.post(url, json={'a': 1})
Defensive patterns

Strategy: validation

Validate before calling

def exactly_one_of(**kwargs):
    present = [k for k, v in kwargs.items() if v is not None]
    if len(present) > 1:
        raise ValueError(f'only one of {list(kwargs)} may be set, got {present}')

# usage:
exactly_one_of(data=data, json=json)
await session.post(url, data=data, json=json)

Type guard

def has_unique_body(data, json_) -> bool:
    return not (data is not None and json_ is not None)

Try / catch

try:
    resp = await session.post(url, data=data, json=json_)
except ValueError as e:
    if 'data and json' in str(e):
        # pick one body form
        resp = await session.post(url, json=json_)
    else:
        raise

Prevention

When it happens

Trigger: Calling session.post(url, data=payload, json=obj). Also fires when one of them is set to a falsy-but-not-None value (e.g. {} or []) combined with the other, since the check is 'is not None'.

Common situations: Refactoring a call that sent form data and tacking on json without removing data. Helper functions that accept **kwargs and forward both. Copy-pasting snippets that each contribute one of the two kwargs.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/cb6943a5f34d4233. Report an issue: GitHub.

Appendix: 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 d041d4d0fd)