{"record":{"id":"72ad7ebcfc70979c","repo":"phacility/phabricator","slug":"when-deleting-almanac-properties-each-property-na","errorCode":null,"errorMessage":"When deleting Almanac properties, each property name must be a string. The value at index \"%s\" is not a string.","messagePattern":"When deleting Almanac properties, each property name must be a string\\. The value at index \"(.+?)\" is not a string\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/almanac/engineextension/AlmanacDeletePropertyEditType.php","lineNumber":21,"sourceCode":"final 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);\n    }\n\n    return $xactions;\n  }\n\n}\n","sourceCodeStart":3,"sourceCodeEnd":37,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/almanac/engineextension/AlmanacDeletePropertyEditType.php#L3-L37","documentation":"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').","triggerScenarios":"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].","commonSituations":"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.","solutions":["Inspect the element at the index named in the message and cast or rebuild the list so every entry is a property-name string.","Validate the payload client-side: every item must be a string before submitting the transaction.","If you meant to delete properties keyed by ID, map IDs to their property name strings first."],"exampleFix":"// before\n$value = array(0 => 'almanac.props.a', 1 => 42);\n\n// after\n$value = array('almanac.props.a', 'almanac.props.b');","handlingStrategy":"validation","validationCode":"$names = [];\nforeach ((array)$raw as $k => $v) {\n  if (!is_string($v)) {\n    continue; // or cast/log\n  }\n  $names[] = $v;\n}\n// send $names as property.delete value; every entry is a string","typeGuard":"function all_strings(array $list): bool {\n  foreach ($list as $item) {\n    if (!is_string($item)) return false;\n  }\n  return true;\n}","tryCatchPattern":null,"preventionTips":["Never push loop counters or IDs into property-name lists; map them to names first.","Sanitize external input (JSON payloads) into list<string> at your API boundary.","Unit-test the delete path with mixed-type input to prove the guard fires."],"tags":["phabricator","almanac","conduit","validation","type-mismatch"],"backgroundTag":"schema-validation-failed","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}