phacility/phabricator · error · Exception

Field "%s" expects a string value, but received a value of t

Error message

Field "%s" expects a string value, but received a value of type "%s".

What it means

Custom commit-message fields validate their Conduit input through DifferentialCommitMessageField::readStringFieldValueFromConduit(). When a field declared to hold a single string receives a non-null value that is not a PHP string (int, float, bool, array), it throws, naming the field key and the received type. This is strict boundary type validation for Conduit methods like 'differential.revision.edit' and commit-message parsing APIs that write string fields (e.g., custom prose fields).

Source

Thrown at src/applications/differential/field/DifferentialCommitMessageField.php:158

        $token = $handle->getCommandLineObjectName();
      }

      $suffix = idx($suffixes, $phid);
      $token = $token.$suffix;

      $out[] = $token;
    }

    return implode(', ', $out);
  }

  protected function readStringFieldValueFromConduit($value) {
    if ($value === null) {
      return $value;
    }

    if (!is_string($value)) {
      throw new Exception(
        pht(
          'Field "%s" expects a string value, but received a value of type '.
          '"%s".',
          $this->getCommitMessageFieldKey(),
          gettype($value)));
    }

    return $value;
  }

  protected function readStringListFieldValueFromConduit($value) {
    if (!is_array($value)) {
      throw new Exception(
        pht(
          'Field "%s" expects a list of strings, but received a value of type '.
          '"%s".',
          $this->getCommitMessageFieldKey(),
          gettype($value)));

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Send the value as a JSON string ("123" instead of 123) for the field key named in the message
  2. Cast values to string client-side before building the Conduit request (e.g., str(val) in Python, String(val) in JS)
  3. If you own the field subclass, override readFieldValueFromConduit() to coerce acceptable types instead of inheriting the strict check
  4. Log the full fields map you are sending to identify which key carries the wrong type

Example fix

// before (Python, Conduit 'differential.revision.edit')
params = {'objectPHID': rev_phid, 'transactions': [{
  'type': 'revision.setfield',
  'value': {'field': 'custom:myfield', 'value': 42},
}]}

// after
params = {'objectPHID': rev_phid, 'transactions': [{
  'type': 'revision.setfield',
  'value': {'field': 'custom:myfield', 'value': '42'},
}]}
Defensive patterns

Strategy: type-guard

Validate before calling

// PHP client: coerce every string-field value before the Conduit call
foreach ($fields as $k => $v) {
  if ($v !== null && !is_string($v)) {
    $fields[$k] = (string)$v;
  }
}

Type guard

function isValidStringFieldValue($value) {
  return $value === null || is_string($value);
}

Try / catch

try {
  $field->readFieldValueFromConduit($value);
} catch (Exception $ex) {
  // The message names the field key and received type;
  // coerce that key to a string and retry once.
}

Prevention

When it happens

Trigger: Passing an integer (e.g., 123 instead of '123') or an array (e.g., array('a')) as a string field value in the 'fields' map of a Conduit request; JSON clients that infer types (Python dict with int, JS number); omitting quotes around numeric strings in hand-built JSON payloads.

Common situations: Custom string fields registered via PhabricatorCustomField extensions; third-party tooling posting typed JSON where older versions tolerated loose types; migration scripts that re-encode values and lose string typing.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


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