monicahq/monica · error · Exception
At least one % is missing to have a valid name order.
Error message
At least one % is missing to have a valid name order.
What it means
Second rule of checkNameOrderValidity(): '%' characters must come in pairs (opening and closing delimiters around placeholder names). substr_count(name_order, '%') % 2 == 1 means an odd count — some placeholder is missing its closing (or opening) '%', such as '%first_name' or a stray single '%' in literal text. The saved preference would render broken names, so the service rejects it with \Exception (rendered as a 500).
Source
Thrown at app/Domains/Settings/ManageUserPreferences/Services/StoreNameOrderPreference.php:57
{
$this->data = $data;
$this->validateRules($data);
$this->checkNameOrderValidity();
$this->updateUser();
return $this->author;
}
private function checkNameOrderValidity(): void
{
// there should be at least one variable in the name order
if (substr_count($this->data['name_order'], '%') < 1) {
throw new \Exception('The name order must contain at least one variable.');
}
if (substr_count($this->data['name_order'], '%') % 2 == 1) {
throw new \Exception('At least one % is missing to have a valid name order.');
}
}
private function updateUser(): void
{
$this->author->name_order = $this->data['name_order'];
$this->author->save();
}
}
View on GitHub (pinned to e08e917341)
Solutions
- Pair every placeholder: %first_name% %last_name%
- Remove stray single % characters from literal text
- Lint the value before saving: preg_match_all('/%[^%]+%/', $value) plus an even substr_count check
Example fix
// before $data = ['name_order' => '%first_name %last_name%']; // odd count -> throws // after $data = ['name_order' => '%first_name% %last_name%']; // even count, balanced
Defensive patterns
Strategy: validation
Validate before calling
// Validate before calling: placeholders must be paired (even '%' count)
$nameOrder = $data['name_order'];
$count = substr_count($nameOrder, '%');
if ($count === 0 || $count % 2 === 1) {
throw ValidationException::withMessages([
'name_order' => 'Name order placeholders must be paired, e.g. %first_name% %last_name%.',
]);
} Type guard
function hasBalancedPlaceholders(string $nameOrder): bool
{
$count = substr_count($nameOrder, '%');
return $count > 0 && $count % 2 === 0;
} Try / catch
try {
app(StoreNameOrderPreference::class)->execute($data);
} catch (\Exception $e) {
if (str_contains($e->getMessage(), '% is missing')) {
throw ValidationException::withMessages(['name_order' => $e->getMessage()]);
}
throw $e;
} Prevention
- Insert placeholders as atomic tokens in the editor so delimiters cannot be split
- Run the even-count check client-side before submit
- Reject stray single '%' characters in literal text during input sanitization
- Add form-request validation covering both placeholder rules
When it happens
Trigger: Submitting a name_order with an unbalanced placeholder: '%first_name %last_name%' (missing a closing delimiter), a deleted delimiter from hand-editing, or one stray '%' typed into literal text.
Common situations: Hand-editing the template in the preferences UI, deleting one delimiter while editing, or concatenating placeholder strings incorrectly in scripts.
Related errors
- The name order must contain at least one variable.
- Could not get address book data.
- No address book found
- $e->getMessage()
- The user does not belong to the vault's account.
AI-assisted analysis of monicahq/monica@e08e917341 (2026-08-17).
Data as JSON: /api/errors/77073413257aa8c6.
Report an issue: GitHub.