Skip to content

Add Discord rate limit handling and bump @pulumi/github#84

Merged
localden merged 1 commit intomainfrom
den/discord-rate-limit-handling
Mar 20, 2026
Merged

Add Discord rate limit handling and bump @pulumi/github#84
localden merged 1 commit intomainfrom
den/discord-rate-limit-handling

Conversation

@localden
Copy link
Contributor

Summary

  • Handle Discord API 429 responses with automatic retry honoring retry_after, so role and member sync no longer fails transiently under load
  • Include the underlying error in "Failed to read role" / "Failed to read member roles" messages for easier debugging
  • Bump @pulumi/github to ^6.11.0 (lockfile resolves 6.12.1)

Test plan

  • npx tsx scripts/validate-config.ts passes
  • pulumi preview shows no unexpected diffs
  • Discord role sync completes without rate-limit errors

- Handle 429 responses from Discord API with automatic retry based on
  retry_after, preventing transient failures during role sync
- Surface underlying error details in role and member role read failures
  to make debugging easier
- Bump @pulumi/github to ^6.11.0 (resolves 6.12.1)

:house: Remote-Dev: homespace
@localden localden requested a review from a team as a code owner March 19, 2026 21:46
@github-actions
Copy link

Pulumi Preview

Click to expand preview output
Previewing update (prod):
  pulumi:pulumi:Stack: (same)
    [urn=urn:pulumi:prod::mcp-access::pulumi:pulumi:Stack::mcp-access-prod]
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760673498959945]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-administrators]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760679064535238]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-lead-maintainers]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760682076307516]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-core-maintainers]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760670395039755]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-maintainers]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760656226680903]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-moderators]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760676367597740]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-inspector-maintainers]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1461488567162503189]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-community-managers]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760661985464506]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-reference-servers-maintainers]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760658848252036]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-csharp-sdk]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760769758101595]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-ruby-sdk]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760695821041767]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-java-sdk]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760684643221556]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-use-mcp-maintainers]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760687444758623]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-sdk-maintainers]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760804134752443]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-go-sdk]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json();
                }
          -     return response.json();
          +     throw (lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`));
            };
                }
            ...
                        };
                    }
          -         catch {
          -             throw new Error(`Failed to read role ${id}`);
          +         catch (error) {
          +             throw new Error(`Failed to read role ${id}: ${error}`);
                    }
                };
            ...
    ~ pulumi-nodejs:dynamic:Resource: (update)
        [id=1460760711796887821]
        [urn=urn:pulumi:prod::mcp-access::pulumi-nodejs:dynamic:Resource::discord-role-php-sdk]
      ~ __provider: 
            exports.handler = __f0;
            var __provider = {create: __f1, read: __f2, update: __f3, delete: __f4};
          + function __sleep(__0) {
          +   return (function() {
          +     with({ sleep: __sleep, this: undefined, arguments: undefined }) {
          + return function /*sleep*/(ms) {
          +     return new Promise((resolve) => setTimeout(resolve, ms));
          + };
          +     }
          +   }).apply(undefined, undefined).apply(this, arguments);
          + }
            function __discordFetch(__0, __1) {
              return (function() {
          -     with({ DISCORD_API_BASE: "https://discord.com/api/v10", discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          +     with({ DISCORD_API_BASE: "https://discord.com/api/v10", sleep: __sleep, discordFetch: __discordFetch, this: undefined, arguments: undefined }) {
          - return async function /*discordFetch*/(token, endpoint, options = {}) {
          -     const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          -         ...options,
          -         headers: {
          -             Authorization: `Bot ${token}`,
          -             'Content-Type': 'application/json',
          -             ...options.headers,
          -         },
          -     });
          -     if (!response.ok) {
          -         const error = (await response.json());
          -         throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          -     }
          -     // Handle 204 No Content
          -     if (response.status === 204) {
          -         return undefined;
          + return async function /*discordFetch*/(token, endpoint, options = {}, maxRetries = 5) {
          +     let lastError;
          +     for (let attempt = 0; attempt <= maxRetries; attempt++) {
          +         const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, {
          +             ...options,
          +             headers: {
          +                 Authorization: `Bot ${token}`,
          +                 'Content-Type': 'application/json',
          +                 ...options.headers,
          +             },
          +         });
          +         if (response.status === 429) {
          +             const body = (await response.json());
          +             const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250;
          +             lastError = new Error(`Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})`);
          +             if (attempt < maxRetries) {
          +                 await sleep(retryAfterMs);
          +                 continue;
          +             }
          +             throw lastError;
          +         }
          +         if (!response.ok) {
          +             const error = (await response.json());
          +             throw new Error(`Discord API error: ${error.message} (code: ${error.code})`);
          +         }
          +         // Handle 204 No Content
          +         if (response.status === 204) {
          +             return undefined;
          +         }
          +         return response.json()

... (truncated)

@localden localden merged commit 3a06068 into main Mar 20, 2026
5 checks passed
@localden localden deleted the den/discord-rate-limit-handling branch March 20, 2026 08:08
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