phacility/phabricator · error · Exception
Transaction value when setting Almanac properties must be a
Error message
Transaction value when setting Almanac properties must be a map with property names as keys.
What it means
Thrown by AlmanacSetPropertyEditType::generateTransactions() when a 'property.set' transaction's 'value' is not an array. Unlike property.delete (which wants a list), property.set wants a map of property name => value, e.g. {"almanac.props.foo": "bar"}. Each map entry becomes one transaction with the key stored in 'almanac.property' metadata and the map value as the new value.
Source
Thrown at src/applications/almanac/engineextension/AlmanacSetPropertyEditType.php:12
<?php
final class AlmanacSetPropertyEditType
extends PhabricatorEditType {
public function generateTransactions(
PhabricatorApplicationTransaction $template,
array $spec) {
$value = idx($spec, 'value');
if (!is_array($value)) {
throw new Exception(
pht(
'Transaction value when setting Almanac properties must be a map '.
'with property names as keys.'));
}
$xactions = array();
foreach ($value as $property_key => $property_value) {
$xactions[] = $this->newTransaction($template)
->setMetadataValue('almanac.property', $property_key)
->setNewValue($property_value);
}
return $xactions;
}
}
View on GitHub (pinned to 5720a38cfe)
Solutions
- Send value as an object/map keyed by property name: {"almanac.props.foo": "bar"}.
- For setting multiple properties, put all keys in one map: {"almanac.props.a": 1, "almanac.props.b": 2}.
- To REMOVE properties, switch the transaction type to property.delete with a list of names instead of abusing property.set.
Example fix
// before
array('type' => 'property.set', 'value' => array('almanac.props.custom')),
// after
array('type' => 'property.set',
'value' => array('almanac.props.custom' => 'some-value'))) Defensive patterns
Strategy: validation
Validate before calling
// property.set wants map<string,mixed>
if (!is_array($value) || ($value !== [] && array_keys($value) === range(0, count($value) - 1))) {
throw new InvalidArgumentException(
'property.set value must be a map of property name => value');
} Type guard
function is_property_set_map(mixed $v): bool {
return is_array($v)
&& ($v === [] || !array_is_list($v));
} Prevention
- Keep a typed wrapper for each edit-engine transaction type so shape mistakes are impossible at call sites.
- Remember deleting uses a LIST, setting uses a MAP; document both next to each other.
- When serializing from languages without distinct list/map types (Python, old PHP), assert keys are strings before submission.
When it happens
Trigger: Calling almanac.device.edit / almanac.service.edit with {"type":"property.set","value":["almanac.props.foo"]} (a list, as property.delete uses); passing a bare string "almanac.props.foo=bar"; passing null; sending a JSON-encoded string instead of an object.
Common situations: Symmetry confusion between property.set (map) and property.delete (list) in the same edit request; clients that cannot emit empty objects and send "[]" or "null"; hand-built query strings that lose the object shape.
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
- When deleting Almanac properties, each property name must be
- 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/4e11085905ba7d15.
Report an issue: GitHub.