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

  1. Pair every placeholder: %first_name% %last_name%
  2. Remove stray single % characters from literal text
  3. 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

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


AI-assisted analysis of monicahq/monica@e08e917341 (2026-08-17). Data as JSON: /api/errors/77073413257aa8c6. Report an issue: GitHub.