phacility/phabricator · error · Exception

Transaction value when deleting Almanac properties must be a

Error message

Transaction value when deleting Almanac properties must be a list of property names.

What it means

Thrown by AlmanacDeletePropertyEditType::generateTransactions() when a 'property.delete' transaction is applied to an Almanac object (device, service, binding, network) via the edit engine / Conduit and its 'value' is not a PHP array. The edit field's Conduit parameter type is ConduitStringListParameterType, so the engine expects a JSON list of property names, e.g. ["almanac.props.foo"]. Any scalar, map, or null value is rejected before transactions are generated.

Source

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

<?php

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);

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Wrap the property names in a JSON list: value ["almanac.props.foo"] even for a single property.
  2. Do not use the property.set map shape; property.delete takes an ordered list of name strings only.
  3. If you want to clear a property, use property.delete with its name in a list rather than sending null.

Example fix

// before
 array('type' => 'property.delete', 'value' => 'almanac.props.custom'),

// after
 array('type' => 'property.delete', 'value' => array('almanac.props.custom')),
// multiple: array('almanac.props.a', 'almanac.props.b')
Defensive patterns

Strategy: validation

Validate before calling

// Before sending an 'almanac.*.edit' request with property.delete:
if (!array_is_list($value) || !is_array($value)) {
  $value = array_values(array_map('strval', (array)$value));
}
foreach ($value as $name) {
  if (!is_string($name) || $name === '') {
    throw new InvalidArgumentException('property.delete needs a list of non-empty property name strings');
  }
}

Type guard

function is_property_delete_list(mixed $v): bool {
  return is_array($v)
    && ($v === [] || array_is_list($v))
    && array_reduce($v, fn($ok, $x) => $ok && is_string($x) && $x !== '', true);
}

Prevention

When it happens

Trigger: Calling almanac.device.edit (or service/bindings edit) with {"type":"property.delete","value":"almanac.props.foo"} (a bare string instead of a one-element list); passing a map {"almanac.props.foo": true} like the property.set format; passing null when intending a no-op delete; passing an object produced by a JSON client that serializes lists as maps.

Common situations: Reusing the value shape from property.set (map) for property.delete (list); client languages where arrays and maps are the same type (older PHP arrays with non-sequential keys, Python dict vs list confusion); copy/paste between almanac.property.remove style examples and the actual property.delete key.

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/aeadf7e5109b6b2e. Report an issue: GitHub.