phacility/phabricator · error · Exception

Use form-encoded data to submit parameters to Conduit endpoi

Error message

Use form-encoded data to submit parameters to Conduit endpoints. Sending a JSON-encoded body and setting 'Content-Type': 'application/json' is not currently supported.

What it means

decodeConduitParams() in PhabricatorConduitAPIController inspects the Content-Type header of requests to /api/ endpoints and hard-rejects 'application/json'. The Conduit HTTP protocol expects parameters as form fields (notably a 'params' field containing a JSON-encoded dictionary), so a raw JSON document body cannot be parsed and is rejected up front.

Source

Thrown at src/applications/conduit/controller/PhabricatorConduitAPIController.php:634

      $value = $json->encodeFormatted($value);
    }

    $value = phutil_tag(
      'pre',
      array('style' => 'white-space: pre-wrap;'),
      $value);

    return $value;
  }

  private function decodeConduitParams(
    AphrontRequest $request,
    $method) {

    $content_type = $request->getHTTPHeader('Content-Type');

    if ($content_type == 'application/json') {
      throw new Exception(
        pht('Use form-encoded data to submit parameters to Conduit endpoints. '.
            'Sending a JSON-encoded body and setting \'Content-Type\': '.
            '\'application/json\' is not currently supported.'));
    }

    // Look for parameters from the Conduit API Console, which are encoded
    // as HTTP POST parameters in an array, e.g.:
    //
    //   params[name]=value&params[name2]=value2
    //
    // The fields are individually JSON encoded, since we require users to
    // enter JSON so that we avoid type ambiguity.

    $params = $request->getArr('params', null);
    if ($params !== null) {
      foreach ($params as $key => $value) {
        if ($value == '') {
          // Interpret empty string null (e.g., the user didn't type anything

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Send the request as application/x-www-form-urlencoded with the whole parameter dictionary JSON-encoded into a single 'params' form field.
  2. Add an 'output': 'json' form field if you want a JSON response.
  3. Prefer arc call-conduit or the SSH conduit interface, which handle encoding for you.

Example fix

# before
import requests
requests.post(endpoint, json={'constraints': {'ids': [1]}})
# after
import json, requests
requests.post(endpoint, data={
    'params': json.dumps({'constraints': {'ids': [1]}}),
    'output': 'json',
})
Defensive patterns

Strategy: validation

Validate before calling

# Python: assert the encoding contract before sending.
import json, requests

def conduit_call(endpoint, token, method, params):
    body = {
        'params': json.dumps(params),   # whole dict JSON-encoded, form field
        'output': 'json',
    }
    r = requests.post(endpoint + method, data=body,
                      headers={'Content-Type': 'application/x-www-form-urlencoded'})
    r.raise_for_status()
    return json.loads(r.text)['result']

Try / catch

# Check the response body rather than HTTP status: Conduit signals errors in JSON.
resp = requests.post(url, data=body)
payload = resp.json()
if payload.get('error_code') == 'ERR-INVALID-AUTH':
    ...  # token/header problem
if not payload.get('error_code') is None:
    raise RuntimeError(payload['error_info'])

Prevention

When it happens

Trigger: Python requests.post(url, json={...}) which sets Content-Type: application/json; JS fetch with body JSON.stringify(...) and a JSON content-type header; curl -H 'Content-Type: application/json' -d '{...}'.

Common situations: Writing a new API client by analogy with ordinary REST APIs where JSON bodies are standard; HTTP wrapper libraries that silently switch to JSON serialization; porting integrations from systems that accept both encodings.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/7b324e2714c0394a. Report an issue: GitHub.