{"record":{"id":"aeadf7e5109b6b2e","repo":"phacility/phabricator","slug":"transaction-value-when-deleting-almanac-properties","errorCode":null,"errorMessage":"Transaction value when deleting Almanac properties must be a list of property names.","messagePattern":"Transaction value when deleting Almanac properties must be a list of property names\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/almanac/engineextension/AlmanacDeletePropertyEditType.php","lineNumber":12,"sourceCode":"<?php\n\nfinal class AlmanacDeletePropertyEditType\n  extends PhabricatorEditType {\n\n  public function generateTransactions(\n    PhabricatorApplicationTransaction $template,\n    array $spec) {\n\n    $value = idx($spec, 'value');\n    if (!is_array($value)) {\n      throw new Exception(\n        pht(\n          'Transaction value when deleting Almanac properties must be a list '.\n          'of property names.'));\n    }\n\n    $xactions = array();\n    foreach ($value as $idx => $property_key) {\n      if (!is_string($property_key)) {\n        throw new Exception(\n          pht(\n            'When deleting Almanac properties, each property name must '.\n            'be a string. The value at index \"%s\" is not a string.',\n            $idx));\n      }\n\n      $xactions[] = $this->newTransaction($template)\n        ->setMetadataValue('almanac.property', $property_key)\n        ->setNewValue(true);","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/almanac/engineextension/AlmanacDeletePropertyEditType.php#L1-L30","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap the property names in a JSON list: value [\"almanac.props.foo\"] even for a single property.","Do not use the property.set map shape; property.delete takes an ordered list of name strings only.","If you want to clear a property, use property.delete with its name in a list rather than sending null."],"exampleFix":"// before\n array('type' => 'property.delete', 'value' => 'almanac.props.custom'),\n\n// after\n array('type' => 'property.delete', 'value' => array('almanac.props.custom')),\n// multiple: array('almanac.props.a', 'almanac.props.b')","handlingStrategy":"validation","validationCode":"// Before sending an 'almanac.*.edit' request with property.delete:\nif (!array_is_list($value) || !is_array($value)) {\n  $value = array_values(array_map('strval', (array)$value));\n}\nforeach ($value as $name) {\n  if (!is_string($name) || $name === '') {\n    throw new InvalidArgumentException('property.delete needs a list of non-empty property name strings');\n  }\n}","typeGuard":"function is_property_delete_list(mixed $v): bool {\n  return is_array($v)\n    && ($v === [] || array_is_list($v))\n    && array_reduce($v, fn($ok, $x) => $ok && is_string($x) && $x !== '', true);\n}","tryCatchPattern":null,"preventionTips":["Encode the two Almanac property transaction shapes in one place: property.set = map, property.delete = list of strings.","Add a client-side schema check (list of strings) before any property.delete call.","Cover both shapes with a fixture in your Conduit integration tests."],"tags":["phabricator","almanac","conduit","edit-engine","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}