phacility/phabricator · error · Exception

When deleting Almanac properties, each property name must be

Error message

When deleting Almanac properties, each property name must be a string. The value at index "%s" is not a string.

What it means

Thrown by AlmanacDeletePropertyEditType::generateTransactions() while iterating a 'property.delete' value list: each element must be a string property name, and the element at the reported index is not. This catches lists that contain integers, nested arrays, booleans, or nulls before a transaction is generated with an invalid metadata key ('almanac.property').

Source

Thrown at src/applications/almanac/engineextension/AlmanacDeletePropertyEditType.php:21

final class AlmanacDeletePropertyEditType
  extends PhabricatorEditType {

  public function generateTransactions(
    PhabricatorApplicationTransaction $template,
    array $spec) {

    $value = idx($spec, 'value');
    if (!is_array($value)) {
      throw new Exception(
        pht(
          'Transaction value when deleting Almanac properties must be a list '.
          'of property names.'));
    }

    $xactions = array();
    foreach ($value as $idx => $property_key) {
      if (!is_string($property_key)) {
        throw new Exception(
          pht(
            'When deleting Almanac properties, each property name must '.
            'be a string. The value at index "%s" is not a string.',
            $idx));
      }

      $xactions[] = $this->newTransaction($template)
        ->setMetadataValue('almanac.property', $property_key)
        ->setNewValue(true);
    }

    return $xactions;
  }

}

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Inspect the element at the index named in the message and cast or rebuild the list so every entry is a property-name string.
  2. Validate the payload client-side: every item must be a string before submitting the transaction.
  3. If you meant to delete properties keyed by ID, map IDs to their property name strings first.

Example fix

// before
$value = array(0 => 'almanac.props.a', 1 => 42);

// after
$value = array('almanac.props.a', 'almanac.props.b');
Defensive patterns

Strategy: validation

Validate before calling

$names = [];
foreach ((array)$raw as $k => $v) {
  if (!is_string($v)) {
    continue; // or cast/log
  }
  $names[] = $v;
}
// send $names as property.delete value; every entry is a string

Type guard

function all_strings(array $list): bool {
  foreach ($list as $item) {
    if (!is_string($item)) return false;
  }
  return true;
}

Prevention

When it happens

Trigger: Sending {"type":"property.delete","value":[0,1]} (list of numeric indices); a JSON list containing null or a nested object; a PHP caller building the list with array_keys on a non-string-keyed array; a client that encodes a single-element tuple as ["name", null].

Common situations: Porting code from an API that accepted index/value pairs; debugging code that accidentally pushes the loop counter into the value list; language serializers that turn mixed arrays into JSON arrays with null placeholders.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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