From a5295c5dd76f3cd9e2bf43463a8f90be9e645b80 Mon Sep 17 00:00:00 2001 From: mARTin Date: Mon, 23 Mar 2026 00:47:52 +0100 Subject: [PATCH] fix: retry Outlook API calls on 429 throttling with backoff Respects Retry-After header when available, otherwise uses exponential backoff (2s, 4s, 6s) for up to 3 attempts. v1.57.11 Co-Authored-By: Claude Opus 4.6 --- package.json | 2 +- src/lib/outlook-calendar.ts | 36 ++++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index b2910d7..0f1615b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.57.10", + "version": "1.57.11", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "main": "index.js", "scripts": { diff --git a/src/lib/outlook-calendar.ts b/src/lib/outlook-calendar.ts index 9a51b0e..e04dea0 100644 --- a/src/lib/outlook-calendar.ts +++ b/src/lib/outlook-calendar.ts @@ -152,20 +152,32 @@ export const getUpcomingEvents = async ( '$top': '50' }); - const response = await fetch( - `${GRAPH_ENDPOINT}/me/calendars/${encodeURIComponent(calendarId)}/calendarView?${params.toString()}`, - { - headers: { - 'Authorization': `Bearer ${accessToken}`, - 'Prefer': 'outlook.timezone="UTC"' + let response: Response | null = null; + for (let attempt = 0; attempt < 3; attempt++) { + response = await fetch( + `${GRAPH_ENDPOINT}/me/calendars/${encodeURIComponent(calendarId)}/calendarView?${params.toString()}`, + { + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'Prefer': 'outlook.timezone="UTC"' + } } - } - ); + ); - if (!response.ok) { - const errorText = await response.text(); - console.error(`[OUTLOOK] Failed to fetch events for calendar ${calendarId}:`, response.status, response.statusText, errorText); - throw new Error(`Failed to fetch events: ${response.status} ${response.statusText}`); + if (response.status === 429) { + const retryAfter = parseInt(response.headers.get('Retry-After') || '', 10); + const delay = (retryAfter > 0 ? retryAfter : (attempt + 1) * 2) * 1000; + console.warn(`[OUTLOOK] 429 throttled for calendar ${calendarId}, retrying in ${delay}ms (attempt ${attempt + 1}/3)`); + await new Promise(r => setTimeout(r, delay)); + continue; + } + break; + } + + if (!response!.ok) { + const errorText = await response!.text(); + console.error(`[OUTLOOK] Failed to fetch events for calendar ${calendarId}:`, response!.status, response!.statusText, errorText); + throw new Error(`Failed to fetch events: ${response!.status} ${response!.statusText}`); } const data = await response.json();