#7975·monica

"Create annual reminder" on a life event silently fails for every built-in life event type

Author: the-d-bCreated Aug 31, 2026Updated Aug 31, 2026
Labelsbug

⚠️ This issue respects the following points: ⚠️

  • This is a bug, not a question or a configuration/webserver/proxy issue.
  • This issue is not already reported on Github (I've searched it).
  • I agree to follow Monica's Code of Conduct.

Bug description

Checking "create annual reminder" when adding a life event silently does nothing if the life event uses one of Monica's built-in/default life event types (e.g. "Got married," "New job," "Moved," etc. — any type you didn't create yourself under Settings → Life Event Types). The life event itself saves normally, but no row is ever created in reminders, and no error is shown anywhere in the UI.

Steps to reproduce

  1. Fresh (or existing) account using the built-in life event categories/types seeded at account creation.
  2. Add a life event to any contact, picking one of the built-in types (e.g. "Got married" under Relationship).
  3. Check "Create a reminder for this every year."
  4. Save.
  5. Check the contact's upcoming reminders (dashboard, or GET /api/reminders/upcoming/{month}), and the reminders table directly.

Expected behavior

Expected: a reminders row exists for this life event, frequency_type = year, and it appears in the upcoming-reminders list once its date is within range.

Actual: no reminders row is ever created. life_events.reminder_id stays NULL. No error is surfaced in the browser.

Root cause

  • PopulateLifeEventsTable::feedLifeEventType() (app/Services/Auth/Population/PopulateLifeEventsTable.php) seeds every built-in LifeEventType for an account with only default_life_event_type_key set — name is left NULL by design, since the display string comes from a translation lookup (people.life_event_sentence_{key}), not the name column.
  • CreateLifeEvent::addYearlyReminder() (app/Services/Contact/LifeEvent/CreateLifeEvent.php) builds the reminder's data with 'title' => $lifeEvent->lifeEventType->name. For any built-in type this is NULL.
  • CreateReminder::rules() (app/Services/Contact/Reminder/CreateReminder.php) requires 'title' => 'required|string|max:100000', so reminder creation throws a ValidationException.
  • This happens after $lifeEvent->save() has already run, so the life event persists with reminder_id still NULL — there's no transaction wrapping the two operations.
  • In the web UI, LifeEventsController::store() catches the exception generically and returns back()->withErrors(...) (a redirect), but the request is submitted via axios.post() from CreateLifeEvent.vue with no .catch() handler — so the failure is swallowed client-side with no visible error to the user.
  • Confirmed directly against the DB: 11 life events created via the UI (all built-in types, "create reminder" checked each time) all have reminder_id = NULL, and none exist in reminders.
  • Also reproduced directly via the REST API (bypassing the browser entirely): POST /api/lifeevents with has_reminder: true on a built-in life_event_type_id returns: {"error": {"message": ["The title field is required."], "error_code": 32}}
  • confirming the failure is server-side, not a frontend issue.

Suggested fix

In CreateLifeEvent::addYearlyReminder(), fall back to the resolved/translated display name instead of the raw name column when it's null (mirroring what the Vue frontend already does for display). Alternatively/additionally, wrap the reminder creation in the same DB transaction as the life event save so a failure rolls back the life event too instead of leaving an inconsistent reminder_id = NULL row — and surface the error to the user either way (add a .catch() in CreateLifeEvent.vue, and/or have LifeEventsController::store() return a JSON error response instead of a redirect, since it's invoked via axios, not a normal form submit).

Workaround

Create a custom life event type (Settings → Life Event Types) and use that instead of a built-in type — custom types require and store a real name, so they don't hit the null-title path. Confirmed working.

Environment

Your own self-hosted instance (monica v4)

Version of Monica

4.1.2

Installation method

Docker image

Web server

None

Database engine version

MariaDB

Additional info

No response