Tell the host to enable the calendar API, rather than showing an empty picker - #6
Merged
Conversation
…y picker Creating OAuth credentials does not enable the Google Calendar API, so the first calendar a self-hoster connects returns 403 accessNotConfigured. On a 403 `expectOk` matched only insufficientPermissions/insufficient_scope, so this fell through to a generic CalendarApiError — and both listCalendars callers discarded it entirely. The result was a saved connection, an empty calendar picker, and nothing in the product or the log saying why. The obvious response to that is to reconnect, which cannot help: the grant is valid and the API is simply off. Reconnecting lands the host back on the same empty picker, so the failure reads as a bug in the product rather than a setting in their own console. Classify it as CalendarSetupRequiredError, deliberately distinct from CalendarReconnectRequiredError because the two need opposite advice, and put the console URL on the error itself — the fix is neither guessable from the status code nor performable inside Punctual. The connections page renders it and offers no Reconnect button, since that is the one action known not to work. Both call sites now log the reason instead of swallowing it; the grant is still kept on any failure, as before.
Contributor
|
Merged — thank you. This is exactly the kind of first-run failure that never shows up on a developer's own machine, and the separation from needs-reconnect is the right call. One follow-up on main: the Microsoft hint now says delegated permission rather than application permission, since that is what this app requests. The (user_id, provider) duplicate-connection issue you mention in "Not included" is real and welcome as a separate PR if you feel like it; otherwise it is on our list. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I was setting things up an forgot to enable the google calendar API. This resulted in some awkward stuff in the UI rather than a clear error. Feel free to toss / reimplement.
What happens today
Creating OAuth credentials in Google Cloud does not enable the Calendar API. So the very first calendar a fresh self-hoster connects returns:
expectOkonly treats a 403 as reconnect-required when the body matchesinsufficientPermissions|insufficient_scope|ErrorAccessDenied.accessNotConfiguredmatches none of those, so it became a genericCalendarApiError— and bothlistCalendarscallers discarded it:catch {}, which kept the grant (correctly) but dropped the reasonlistCalendarsSafely, which returned[]The host ends up with a saved connection, an empty calendar picker, and nothing in the product or the log explaining it. I hit this on a fresh deploy and, having no other signal, reconnected twice — which cannot work, because the grant was valid the whole time. An empty picker is also indistinguishable from an account that genuinely has no calendars.
It's worth noting this is a first-run failure rather than an exotic one: every new deployment hits it unless the operator already knew to enable the API.
What this changes
CalendarSetupRequiredError, deliberately separate fromCalendarReconnectRequiredError. The two need opposite advice, and telling someone to reconnect a perfectly valid grant sends them in a loop they have no way to diagnose. It carrieshowToFix— the console URL — because the fix is neither guessable from the status code nor performable inside Punctual.isApiNotEnabled(body), pure and unit-tested. Google reports the same condition three ways (accessNotConfigured,SERVICE_DISABLED, or prose naming the project) depending on which surface answers. It deliberately does not match a bareis disabled, which shows up in unrelated 403s — a false positive here would send a host to change a console setting that was never the problem.SETUP_HINTScovers Microsoft too, thoughaccessNotConfiguredis Google-specific in practice — Entra's equivalent is an unconsented application permission.Tests
6 new, all in
test/core(pure, no Workers runtime):npm run typecheckclean,npm test880 passing locally.Not included
The same deploy surfaced a second bug I've left out to keep this focused, and I'm happy to send it separately if useful:
calendar_connectionshas no unique constraint on(user_id, provider)and the connect handler always inserts, so the reconnect loop above silently created two Google connections for one account. They can't be told apart either, sinceprovider_account_emailis''—GOOGLE_CALENDAR_SCOPESomitsopenid/email, so Google returns noid_tokenforemailFromIdTokento read. Adding those two non-sensitive scopes and upserting on(user_id, provider, provider_account_email)would fix it without blocking a genuine work+personal setup.