Repository navigation
[Bug]: ICS calendar events are silently dropped when a property parameter is quoted and contains a delimiter #2313
Description
Bug Description
Note: I worked with claude to figure out why a calendar event was not displaying, and this is the bug it found. The bug report is generated by claude.
What happened
A confirmed 4-hour meeting on my Google Calendar never appeared anywhere in TaskNotes — not in the calendar view, not in icsSubscriptionService.getAllEvents(). No error was surfaced, and lastError for the subscription was empty. I only found it by diffing the raw .ics feed against the plugin's cache.
Of 1,323 VEVENTs in my feed, 697 of 854 distinct UIDs were cached. Restricting to events in the future (where nothing is pruned), 21 events, 20 cached, 1 missing.
The one missing future event is the only one of the 21 carrying a quoted parameter value that contains a delimiter character:
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=DECLINED;CN=someone@example.org;X-NUM-GUESTS=0;X-RESPONSE-COMMENT="Appreciate the invite but Kate and I are hosting our closing event all day on the 14th so won't be able to make it. Not sure of Dan's availability but you could add him and see if he is free.":mailto:someone@example.org
Note the escaped semicolons (;) inside the double-quoted parameter value. Google Calendar emits this whenever an invitee declines with a free-text comment containing a semicolon or apostrophe-escaped text.
Per RFC 5545 §3.2, a quoted param value is delimited by DQUOTE, so ; and : inside it must not be treated as separators. If the ICS line parser splits on ;/: without tracking quote state, this line shreds into garbage parameters and — going by the observed behaviour — takes the entire VEVENT down with it.
Why it matters
The failure is completely silent, and the trigger is another person's RSVP comment. Nothing the calendar owner does can cause or prevent it, and there is no signal that an event is missing. In my case a 41-attendee, 4-hour convening was invisible for weeks; I planned around an empty slot that was not empty.
Steps to Reproduce
Steps to reproduce
Subscribe to an ICS feed containing a VEVENT like the one below.
Refresh the subscription and wait for the cache to settle.
The event does not appear in the calendar view or getAllEvents().
BEGIN:VEVENT
UID:repro-quoted-param@example.com
DTSTART:20260914T140000Z
DTEND:20260914T180000Z
SUMMARY:Should appear but does not
STATUS:CONFIRMED
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=DECLINED;CN=a@example.com;X-RESPONSE-COMMENT="Sorry; I have a conflict":mailto:a@example.com
END:VEVENT
Removing only the X-RESPONSE-COMMENT parameter should make the event appear.
Expected
The event is parsed and displayed. Unparseable parameters should at worst be ignored, never discard the whole VEVENT — and a parse failure should be surfaced rather than silent.
Caveat on the diagnosis
I identified this by correlation on real data (1 of 1 affected, 20 unaffected controls) plus the RFC reading. I have not stepped through the parser to confirm the exact failure point, and I have not run the minimal repro above against a clean vault. The observation that a specific future event is reproducibly absent from a settled cache is solid; the proposed mechanism is my best inference.
TaskNotes Debug Info
SYSTEM INFO:
Obsidian version: 1.13.7
Installer version: 1.12.7
Operating system: Darwin Kernel Version 25.6.0: Fri Jul 31 19:11:03 PDT 2026; root:xnu-12377.161.14~5/RELEASE_ARM64_T8132 25.6.0
Login status: not logged in
Language: en
Insider build toggle: off
Live preview: on
Base theme: dark
Community theme: AnuPpuccin 1.5.0
Snippets enabled: 1
Restricted mode: off
Plugins installed: 9
Plugins enabled: 9
1: Templater v2.25.0
2: PDF++ v0.40.31
3: Image Converter v1.4.6
4: Periodic Notes v0.0.17
5: TaskNotes v4.12.5
6: Granola Meetings Simple Sync v2.2.1
7: Notebook Navigator v3.3.7
8: TaskNotes Workflows v0.1.4
9: Omnisearch v1.31.0
Affected Task Frontmatter
Relevant Bases File Content
Relevant Settings or Customizations
No response
Console Errors or Logs
Screenshots or Screen Recording
No response
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsCompleted
Activity
callumalpass commentedon Sep 13, 2026
Thanks @benschifman. I reproduced the missing event with your example on TaskNotes 4.12.5, but the quoted comment does parse successfully. Removing only
X-RESPONSE-COMMENTstill leaves the event hidden; changingPARTSTAT=DECLINEDtoACCEPTEDmakes it appear.The current ICS import hides an event if any attendee is marked declined, without checking whether that attendee is the calendar owner. That can hide a meeting because another guest declined it.
Could you confirm whether the declined attendee is someone other than the calendar owner? If possible, please share a minimal sanitized VEVENT preserving the ORGANIZER and ATTENDEE parameters and indicate which anonymized attendee is the owner. Please do not share the private calendar URL, access tokens, or the full calendar. This will help distinguish another guest's decline from an event the owner actually declined.
benschifman commentedon Sep 13, 2026
Thank you. (And as an aside, thanks for creating Tasknotes, which I use every day and get great value from!) I fed your response to claude in the session where I was looking into why some events were off, claude says:
Thanks for reproducing this so quickly — and you're right, my diagnosis of the mechanism was wrong. I found the quoted
X-RESPONSE-COMMENTby correlation and inferred a parser problem; the real correlate is that guests who decline tend to leave comments. Apologies for pointing you at the wrong line.Confirmed: the declined attendees are all other guests, not the calendar owner. Details from the live feed owner is masked below as
owner@example.com):ACCEPTEDACCEPTEDDECLINED(owner)I also ran it across every future event in the feed: every event with a non-owner
DECLINEDis missing from the cache; every event with no declines is present. So your reading — "hides the event if any attendee declined, without checking whether that attendee is the owner" — matches the data exactly.The practical effect is worth spelling out: on a shared team calendar, any meeting where one colleague can't make it disappears for everyone else. The two hidden events above are a 40-person meeting and a routine team meeting; in both cases the owner had accepted.
Minimal sanitized VEVENT
Structure and parameters are verbatim from the real event; emails, CNs, the comment text and UID are replaced.
owner@example.comis the calendar owner (the account whose private ICS URL is subscribed). Onlyguest-declinedandguest-declined-2have declined.Expected: visible, since the owner accepted. Actual on 4.12.5: hidden.
One design note, in case it's useful: Google's private ICS export doesn't carry an explicit "this is the subscriber" marker, but the feed's
X-WR-CALNAMEis the owner's address (bowner@example.comin my case), so matching the owner'sATTENDEEline against that is one way to find it. Where no attendee matches the owner at all — e.g. the owner created the event and is theORGANIZER— there's nobody's decline to honour and the event should just show.callumalpass commentedon Sep 21, 2026
Resolved in 4.13.1. Thanks @benschifman for reporting this. I’m closing issue #2313.