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
- 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.
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
- 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.
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
- Service type "%s" is unrecognized. Valid types are: %s.
- Transaction value when deleting Almanac properties must be a
- Transaction value when setting Almanac properties must be a
- Service "%s" is unrecognized, restricted, or you do not have
- Almanac service, device, property, network and namespace nam
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/72ad7ebcfc70979c.
Report an issue: GitHub.