Skip to content

minor: null check#242

Open
chollinger93 wants to merge 1 commit intotchapi:mainfrom
chollinger93:chollinger93/null-calendar-fix
Open

minor: null check#242
chollinger93 wants to merge 1 commit intotchapi:mainfrom
chollinger93:chollinger93/null-calendar-fix

Conversation

@chollinger93
Copy link

@chollinger93 chollinger93 commented Feb 15, 2026

I had auto updates on the container on and the edge tag set - not my wisest decision - and I assume something got itself into a bad state.

In either case, my Mac refused to sync to Davis with

php.ERROR: Warning: Undefined array key "id" {"exception":"[object] (ErrorException(code: 0): Warning: Undefined array key \"id\" at /var/www/davis/src/Plugins/PublicAwareDAVACLPlugin.php:57)"} []
php.ERROR: Warning: Trying to access array offset on null {"exception":"[object] (ErrorException(code: 0): Warning: Trying to access array offset on null at /var/www/davis/src/Plugins/PublicAwareDAVACLPlugin.php:57)"} []

So this adds a null check to PublicAwareDAVACLPlugin

I tested this via

docker buildx build \
  --platform linux/amd64 \
  -f docker/Dockerfile-standalone \
  -t ${DOCKER_REGISTRY}/davis-standalone:fixed \
   --build-arg fpm_user=82:82 \
  --push \
  .

I run mySQL on a remote instance, so my compose file is just davis-standalone.

@tchapi
Copy link
Owner

tchapi commented Feb 16, 2026

Hi @chollinger93

Thanks for looking at that !
I have a hard time understanding how the calendarInfo could be null .. would you mind sharing a var_dump of the calendar object (ie $node) when this happens, so I can have a look (you can anonymize the data of course)?

Thanks!

@chollinger93
Copy link
Author

I did add a log in a subsequent commit as such:

 $this->logger->error('PublicAwareDAVACLPlugin: Unexpected calendarInfo structure', [
                            'nodeClass' => get_class($node),
                            'calendarInfo' => json_encode($calendarInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES),
                            'calendarInfoType' => gettype($calendarInfo),
                            'node' => json_encode($node, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES),
                        ]);

AI Slop warning, I haven't touched PHP since ca 2004, so I had AI do this logging/encoding (in case it did something stupid).

But all I got was

app.ERROR: PublicAwareDAVACLPlugin: Unexpected calendarInfo structure {"nodeClass":"Sabre\\CalDAV\\Schedule\\SchedulingObject","calendarInfo":"[]","calendarInfoType":"array","node":"{}"} []

I poked around the mySQL database but didn't see anything blatantly wrong the the schedulingobjects table (except for the fact that the row with ID 1 is missing).

What I do see in the Mac calendar app is this

image

@tchapi
Copy link
Owner

tchapi commented Feb 16, 2026

Ah, interesting. I'll have a look. Thanks for the extra info!

@tchapi
Copy link
Owner

tchapi commented Feb 16, 2026

What I do see in the Mac calendar app is this

You confirm that you see this without your fix, but that your fix actually makes this error disappear, right?

@tchapi tchapi self-assigned this Feb 16, 2026
@tchapi
Copy link
Owner

tchapi commented Feb 16, 2026

Finally, would you mind sending me the content of one or two rows of schedulingobjects that you have in your DB wit the related object (ie the ones referenced in uri, and principaluri)? Again, it can be anonymised 🙏🏼

@chollinger93
Copy link
Author

What I do see in the Mac calendar app is this

You confirm that you see this without your fix, but that your fix actually makes this error disappear, right?

Nope, with my fix, it just doesn't crash and the calendar works - I still see the error on MacOS, but the calendar does sync. It does seem like my Nextcloud CalDAV does the same, though.

Re database rows... this is a bit tricky due to the nature of the data, so redaction might remove the useful data.

Curiously only 1 result, despite 48 or so schedulingobjects:

select s.*, p.*, c.* from schedulingobjects s 
join principals p on s.principaluri = p.uri 
join calendarobjects c on s.uri = c.uri 

This did have a bunch of X-APPLE-RADIUS etc PII

{
"select s.*, p.*, c.* from schedulingobjects s \njoin principals p on s.principaluri = p.uri \njoin calendarobjects c on s.uri = c.uri ": [
	{
		"id" : 4,
		"principaluri" : "principals\/user002@example.com",
		"calendardata" : "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-\/\/Sabre\/\/Sabre VObject 4.5.7\/\/EN\r\nCALSCALE:GREGORIAN\r\nMETHOD:REQUEST\r\nBEGIN:VTIMEZONE\r\nTZID:America\/New_York\r\nBEGIN:DAYLIGHT\r\nDTSTART:20070311T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r\nTZNAME:EDT\r\nTZOFFSETFROM:-0500\r\nTZOFFSETTO:-0400\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nDTSTART:20071104T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r\nTZNAME:EST\r\nTZOFFSETFROM:-0400\r\nTZOFFSETTO:-0500\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nATTENDEE;CN=User One;CUTYPE=INDIVIDUAL;EMAIL=user001@example.com;PART\r\n STAT=ACCEPTED:mailto:user001@example.com\r\nATTENDEE;CN=User Two;CUTYPE=INDIVIDUAL;EMAIL=user002@example.com;PARTSTA\r\n T=NEEDS-ACTION:mailto:user002@example.com\r\nCREATED:20250910T135815Z\r\nDTEND;TZID=America\/New_York:20250917T114500\r\nDTSTART;TZID=America\/New_York:20250917T074500\r\nLAST-MODIFIED:20250910T135815Z\r\nLOCATION:123 Main St\\nAnytown\\, ST\\, United States\r\nORGANIZER;CN=User One;EMAIL=user001@example.com:mailto:user001@example\r\n .com\r\nSEQUENCE:1\r\nSUMMARY:Appointment\r\nTRANSP:OPAQUE\r\nUID:A01E86CB-31A2-41E7-BCCE-625E6A164FFC\r\nURL;VALUE=URI:\r\nX-APPLE-CREATOR-IDENTITY:com.apple.calendar\r\nX-APPLE-CREATOR-TEAM-IDENTITY:0000000000\r\nDTSTAMP:20250910T135817Z\r\nBEGIN:VALARM\r\nACTION:NONE\r\nTRIGGER;VALUE=DATE-TIME:19760401T005545Z\r\nEND:VALARM\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
		"uri" : "sabredav-7d0a55d8-3a78-4668-b840-7aadfd96179c.ics",
		"lastmodified" : 1758000000,
		"etag" : "b29202ea8a9ae07bedf5f5ee118f4fe4",
		"size" : 1860,
		"id" : 6,
		"uri" : "principals\/user002@example.com",
		"email" : "user002@example.com",
		"displayname" : "User Two",
		"is_main" : 1,
		"is_admin" : 0,
		"id" : 4,
		"calendarid" : 2,
		"calendardata" : "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nCALSCALE:GREGORIAN\r\nPRODID:-\/\/Apple Inc.\/\/iPhone OS 18.6.2\/\/EN\r\nBEGIN:VTIMEZONE\r\nTZID:America\/New_York\r\nBEGIN:DAYLIGHT\r\nDTSTART:20070311T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r\nTZNAME:EDT\r\nTZOFFSETFROM:-0500\r\nTZOFFSETTO:-0400\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nDTSTART:20071104T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r\nTZNAME:EST\r\nTZOFFSETFROM:-0400\r\nTZOFFSETTO:-0500\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nATTENDEE;CN=User One;CUTYPE=INDIVIDUAL;EMAIL=user001@example.com;PART\r\n STAT=ACCEPTED:mailto:user001@example.com\r\nATTENDEE;CN=User Two;CUTYPE=INDIVIDUAL;EMAIL=user002@example.com;PARTSTA\r\n T=NEEDS-ACTION:mailto:user002@example.com\r\nCREATED:20250910T135815Z\r\nDESCRIPTION:STE 100\r\nDTEND;TZID=America\/New_York:20250917T114500\r\nDTSTART;TZID=America\/New_York:20250917T074500\r\nLAST-MODIFIED:20250916T201615Z\r\nLOCATION:123 Main St\\nAnytown\\, ST 12345\\, United States\r\nORGANIZER;CN=User One;EMAIL=user001@example.com:mailto:user001@example\r\n .com\r\nSEQUENCE:1\r\nSUMMARY:Appointment (Town Center)\r\nTRANSP:OPAQUE\r\nUID:A01E86CB-31A2-41E7-BCCE-625E6A164FFC\r\nURL;VALUE=URI:\r\nX-APPLE-CREATOR-IDENTITY:com.apple.calendar\r\nDTSTAMP:20250916T201616Z\r\nBEGIN:VALARM\r\nACTION:NONE\r\nTRIGGER;VALUE=DATE-TIME:19760401T005545Z\r\nEND:VALARM\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
		"uri" : "sabredav-7d0a55d8-3a78-4668-b840-7aadfd96179c.ics",
		"lastmodified" : 1758000000,
		"etag" : "24152e5181f66075cc2f137774d09376",
		"size" : 1326,
		"componenttype" : "VEVENT",
		"firstoccurence" : 1758000000,
		"lastoccurence" : 1758000000,
		"uid" : "A01E86CB-31A2-41E7-BCCE-625E6A164FFC"
	}
]}

@tchapi
Copy link
Owner

tchapi commented Feb 16, 2026

Curiously only 1 result, despite 48 or so schedulingobjects:

💡 Sounds like the problem: you have scheduling objects that are not tied to a calendar object (ie "a containing calendar"), and thus, when sabre/dav fetches them, they don't find the related calendarInfo -> it's thus null, and PHP is not happy to try to access the id of null.

If you look at the scheduling objects in your DB, there's only one with a link to an actual calendar object (via the uri). Do the others have a null uri? Do they have an uri, but there is no calendar object with this uri in your db?

Did you remove things in the DB yourself (manually I mean)? Did you delete calendar objects via your calendar clients in a specific manner?

It may be some kind of bug where we only delete the calendar object and not the related scheduling instance, but I'd like to have a better view of how we end up in this situation :)

@chollinger93
Copy link
Author

chollinger93 commented Feb 16, 2026

💡 Sounds like the problem: you have scheduling objects that are not tied to a calendar object (ie "a containing calendar"), and thus, when sabre/dav fetches them, they don't find the related calendarInfo -> it's thus null, and PHP is not happy to try to access the id of null.

That makes sense to me.

If you look at the scheduling objects in your DB, there's only one with a link to an actual calendar object (via the uri). Do the others have a null uri? Do they have an uri, but there is no calendar object with this uri in your db?

They have a uri but don't match anything.

Did you remove things in the DB yourself (manually I mean)? Did you delete calendar objects via your calendar clients in a specific manner?

Nope! I first connected to this DB by hand today for this thread. But I did, rather foolishly, have had the container on auto-update with the edge tag...

Re calendar clients, it's default iOS and MacOS apps with 2 users, nothing complicated.

The only non-standard thing is that I have davis behind ngrok. I have a traffic policy that enforces OAuth, except for

- name: oauth
          expressions:
            - "!((req.url.path.contains('/dav/') || req.url.path.contains('/.well-known')))"
          actions:
            - type: oauth ...

@tchapi
Copy link
Owner

tchapi commented Feb 17, 2026

Thanks for the context

They have a uri but don't match anything.

Ok so somehow, calendar objects got deleted but not the scheduling objects. It's a bit weird. Do they have an existing principalUri (ie, a uri of a principal that exists in the principals table)?

But I did, rather foolishly, have had the container on auto-update with the edge tag...

Well I don't remember pushing anything "dangerous" on the edge tag recently (It's generally only the tip of main), and nothing that could delete objects randomly like that in any case. I'd of course nudge you to set your version on a release tag but I honestly don't think it's the culprit.

Re calendar clients, it's default iOS and MacOS apps with 2 users, nothing complicated.

I use the same, didn't notice a strange behavior yet

The only non-standard thing is that I have davis behind ngrok

Well it should work well (I trust you to know how to use ngrok :) )

Only thing I can think of is that you deleted a calendar (via the web UI of Davis) and we somehow missed to delete those objects. I'll look. Otherwise it's a sabre/dav bug that I don't know about (somehow related: sabre-io/dav#1138)

@chollinger93
Copy link
Author

Ok so somehow, calendar objects got deleted but not the scheduling objects. It's a bit weird. Do they have an existing principalUri (ie, a uri of a principal that exists in the principals table)?

Yes. The principals table has 3 entries per user

principals/email@example.org/calendar-proxy-read
principals/email@example.org/calendar-proxy-write
principals/email@example.org

The key in the schedule table is always in the last format.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments