Why your CalDAV calendar reminders never fire on Android

The events are there. You can see them in the calendar app, the right ones on the right days, with “10 minutes before” printed on the detail screen. And nothing ever goes off.

This is the most common complaint about self-hosted calendars on Android, and it has at least four different causes that all look identical from the phone. Two of them you cannot fix from the calendar app, whatever it is. Here they are in the order worth checking, with how to tell them apart.

Three jobs, not two

I’ve written before that Android splits calendar work into two jobs: something syncs, something displays. Reminders are where the split has a third part nobody mentions.

DAVx⁵ does the first job only, and says so. The provider stores reminder rows but does not, on its own, put a notification on your screen. So a phone with DAVx⁵ and no calendar app has a complete, correctly synced calendar that is incapable of reminding you of anything.

That is cause zero, and it is the easy one: install a calendar app. The other three are what people hit after they already did that.

Cause 1 — the alarm arrived, flagged as one that doesn’t ring

This is the interesting one, because there is no way to see it from the phone.

Android’s Reminders table has a METHOD column with five values: METHOD_DEFAULT, METHOD_ALERT, METHOD_EMAIL, METHOD_SMS and METHOD_ALARM. In practice only METHOD_ALERT produces a notification. Calendar apps query that value and ignore the rest — AOSP’s does, Calendula does, every one I’ve read does.

Here is the part that took me a while to believe. CalendarContract’s own documentation says something different:

the device will only process METHOD_DEFAULT and METHOD_ALERT reminders (the other types are simply stored so we can send the same reminder info back to the server when we make changes)

So the platform documents METHOD_DEFAULT as a value that rings, and then the platform’s own provider doesn’t honour it: CalendarAlarmManager filters on METHOD = METHOD_ALERT and METHOD_DEFAULT appears nowhere in that file. The contract and the implementation disagree, the implementation wins, and everything downstream has been written against the implementation.

Which value a synced alarm lands under is decided by ical4android, the library DAVx⁵ uses to translate iCalendar into provider rows. The mapping is a single when block in AndroidEvent.insertReminder, and it reads the VALARM’s ACTION property:

ACTION in the VALARM Provider METHOD Rings?
DISPLAY METHOD_ALERT yes
AUDIO METHOD_ALERT yes
EMAIL METHOD_EMAIL no — the server’s job
anything else, or absent METHOD_DEFAULT no

The library’s own comment on that last branch says it: won’t trigger an alarm on the Android device.

So an alarm written by something that left ACTION off — or set it to NONE, or to PROCEDURE — syncs through, appears on the event detail screen as a reminder, and is stored in a way that every calendar app on the device reads as silent. There is no error anywhere. (RFC 5545 does require a VALARM to carry an ACTION, so an alarm missing one is already malformed; it just happens to be malformed in a way the parser tolerates instead of rejecting, which is the whole reason it gets this far.) DAVx⁵ hit exactly this with its own default-reminder setting years ago, and the maintainer’s diagnosis in discussion #491 is the same paragraph I just wrote: the alarms were being created without an action, ical4android mapped them to METHOD_DEFAULT, and Android ignored them.

The fix belongs on whichever side wrote the alarm. A calendar app could in principle fire METHOD_DEFAULT too — the documentation above says it should — but none does, so writing an app that did would mean ringing for rows every other calendar app on the device treats as silent. The row is dead in practice whatever the contract says.

How to check: open the event on your server’s web UI and look at what kind of reminder it is. In Nextcloud, a reminder set to Email is an ACTION:EMAIL alarm — it will never make your phone buzz, by design, and the mail comes from the server instead. If it is set to Notification and still does nothing, you are probably in cause 2.

Cause 2 — the alarm never arrived, because it was invalid

The alarm can also be rejected on the way in, which looks the same from the phone and different in the logs.

RFC 5545 puts requirements on a VALARM that most people setting a reminder in a web UI never see. An ACTION:DISPLAY alarm must carry a DESCRIPTION. Durations cannot mix weeks with hours. Both of those have bitten Nextcloud inside the last few years:

Both of those are server-side bugs that a phone cannot work around. Neither is a reason to distrust the stack in general — they are the kind of thing that gets fixed once someone reports it, and both were.

How to check: the quickest tell needs no tools. If the reminder never made it into the provider, the calendar app isn’t showing it on the event detail screen either — a reminder you can see in the app arrived, one you can’t, didn’t. And if the event itself is missing, that’s the same cause one level up, which is what both bugs above actually looked like.

For the detail, DAVx⁵’s logs are off by default: Settings → turn on Verbose logging, force a sync, then open Show debug infoView logs. A rejected resource also arrives as its own notification, which opens the same place.

Cause 3 — the alarm is fine and Android sat on it

If the reminder is visible on the phone, was set as a notification, and still doesn’t fire, the remaining suspects are all local. In rough order of how often they’re the answer:

How to check: set an event five minutes out with a one-minute reminder, lock the phone, and leave it alone. If it fires there but not overnight, it’s battery optimisation.

What Calendula does about it

I write Calendula, so treat this section as interested. What it can honestly claim is limited to the local half:

It delivers reminders itself rather than reacting to the calendar provider’s broadcast. That sounds like an implementation detail and it is the reason the whole pipeline got rewritten: the provider’s EVENT_REMINDER broadcast holds on stock Android and demonstrably not everywhere. AOSP’s own unbundled calendar accumulated three separate workarounds in this area — two for OEM providers that retargeted the broadcast or wrote the alert row late, one for stale notifications on HTC — and only one of the three survives in the current tree. An app that only reacts cannot tell “nothing was due” from “the broadcast never came”. So Calendula reads the reminder offsets as data, works out when each occurrence is due, and holds a single alarm for the next one — exact where the OS allows it, inexact where the permission above was withheld. A scan that runs late still posts what the missed alarm owed, so a reboot, an app update or a doze window costs you nothing.

There is a Reliable delivery setting that opens the system battery optimisation dialog directly, because cause 3 is a real cause and burying the fix three menus deep in Android’s settings helps nobody.

What it cannot do is cause 1 or cause 2. Calendula queries METHOD_ALERT and nothing else, exactly like every other calendar app, and an alarm that never synced isn’t on the device to fire. If someone tells you their calendar app fixes those, ask them which column they’re reading.

The short version

  1. Is a calendar app installed at all? DAVx⁵ is not one.
  2. Can you see the reminder on the event in the app? If not, it never synced — check the server and the sync log.
  3. Is the reminder a notification on the server, or an email? Email reminders are the server’s job and will never ring the phone.
  4. Everything else is battery optimisation until proven otherwise.

Checked on 20 September 2026 against ical4android main and the linked issues. If one of them has moved on since, tell me and I’ll correct it.