Add GitHub API rate limit handling UI#533
Conversation
✅ Deploy Preview for github-spy ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning Review limit reached
More reviews will be available in 23 minutes and 11 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughActivityFeed now detects GitHub API rate limit responses (HTTP 403), reads the ChangesGitHub API Rate Limit Detection and Error Display
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🎉 Thank you @ananyadarna for your contribution. Please make sure your PR follows https://github.com/GitMetricsLab/github_tracker/blob/main/CONTRIBUTING.md#-pull-request-guidelines
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/ActivityFeed.tsx`:
- Around line 51-55: The error message passed to setError in ActivityFeed.tsx
uses a multiline template literal which introduces unintended whitespace and
newlines; replace it with a single-line interpolated string (e.g. `GitHub API
rate limit exceeded. Please try again after ${resetTime}. Remaining Requests:
${remaining}`) or build the message via concatenation/trim to eliminate extra
spacing before calling setError; update the invocation where setError(...) is
used so the alert text is a compact single-line string.
- Around line 40-60: The code treats any 403 as a rate-limit and can let stale
responses overwrite state; update the fetchEvents logic in ActivityFeed to: 1)
detect rate-limit only when rate-limit headers indicate it (e.g., const
remaining = res.headers.get("X-RateLimit-Remaining"); if (remaining === "0") {
const reset = res.headers.get("X-RateLimit-Reset"); const resetTime = reset ?
new Date(Number(reset) * 1000).toLocaleTimeString() : "Unknown";
setError(`GitHub API rate limit exceeded. Please try again after ${resetTime}.
Remaining Requests: ${remaining}`); } else if (res.status === 403) {
setError("Access forbidden"); } 2) introduce an AbortController and/or a
monotonically increasing requestId inside useEffect around fetchEvents, pass
signal to fetch, abort on cleanup, and ignore responses whose requestId is stale
before calling setEvents/setError/setLoading; and 3) make the rate-limit message
a single-line string to avoid whitespace issues.
- Around line 30-77: fetchEvents can produce out-of-order state updates because
in-flight fetches aren't canceled; update fetchEvents to create an
AbortController per invocation (or a monotonic requestId) and pass its signal to
fetch, store the controller/requestId in the surrounding useEffect scope, and
cancel it in the useEffect cleanup (along with clearInterval). In fetchEvents,
detect aborted requests (e.g., err.name === 'AbortError' or
controller.signal.aborted / mismatched requestId) and return early so you do not
call setEvents, setError, or setLoading(false) for aborted/outdated requests;
ensure the finally block also skips setLoading when the request was aborted. Use
the function name fetchEvents and state setters setEvents, setError, setLoading
to locate the changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: aa1d36fe-eb07-48d5-9bba-29c95cf65525
📒 Files selected for processing (2)
src/components/ActivityFeed.tsxsrc/pages/Activity.tsx
| const fetchEvents = async () => { | ||
| try { | ||
| setLoading(true); | ||
|
|
||
| const res = await fetch( | ||
| `https://api.github.com/users/${username}/events` | ||
| ); | ||
| const data = await res.json(); | ||
|
|
||
| setEvents(data); | ||
| setLoading(false); | ||
| } catch (err) { | ||
| console.error(err); | ||
| setLoading(false); | ||
| } | ||
| try { | ||
| setLoading(true); | ||
| setError(""); | ||
|
|
||
| const res = await fetch( | ||
| `https://api.github.com/users/${username}/events` | ||
| ); | ||
|
|
||
| // Handle GitHub API rate limit | ||
| if (res.status === 403) { | ||
| const remaining = | ||
| res.headers.get("X-RateLimit-Remaining") || "0"; | ||
|
|
||
| const reset = | ||
| res.headers.get("X-RateLimit-Reset"); | ||
|
|
||
| const resetTime = reset | ||
| ? new Date(Number(reset) * 1000).toLocaleTimeString() | ||
| : "Unknown"; | ||
|
|
||
| setError( | ||
| `GitHub API rate limit exceeded. | ||
| Please try again after ${resetTime}. | ||
| Remaining Requests: ${remaining}` | ||
| ); | ||
|
|
||
| setEvents([]); | ||
| setLoading(false); | ||
| return; | ||
| } | ||
|
|
||
| if (!res.ok) { | ||
| throw new Error("Failed to fetch activity"); | ||
| } | ||
|
|
||
| const data = await res.json(); | ||
|
|
||
| setEvents(data); | ||
| } catch (err) { | ||
| console.error(err); | ||
|
|
||
| setError( | ||
| "Something went wrong while fetching GitHub activity." | ||
| ); | ||
| } finally { | ||
| setLoading(false); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ls -la
echo "---- ActivityFeed.tsx (lines 1-200) ----"
sed -n '1,200p' src/components/ActivityFeed.tsx
echo "---- ActivityFeed.tsx (lines 200-400) ----"
sed -n '200,400p' src/components/ActivityFeed.tsxRepository: GitMetricsLab/github_tracker
Length of output: 5532
Guard against stale/out-of-order GitHub fetches in ActivityFeed.
fetchEvents runs on mount and every 30s, but the cleanup only clears the interval; in-flight fetch calls aren’t canceled. When username changes or the component unmounts, earlier responses can still call setEvents/setError/setLoading(false), overwriting newer state. Add an AbortController (or request-id guard) canceled in cleanup, and skip all state updates (including setLoading(false) in finally) for aborted/outdated requests.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/ActivityFeed.tsx` around lines 30 - 77, fetchEvents can
produce out-of-order state updates because in-flight fetches aren't canceled;
update fetchEvents to create an AbortController per invocation (or a monotonic
requestId) and pass its signal to fetch, store the controller/requestId in the
surrounding useEffect scope, and cancel it in the useEffect cleanup (along with
clearInterval). In fetchEvents, detect aborted requests (e.g., err.name ===
'AbortError' or controller.signal.aborted / mismatched requestId) and return
early so you do not call setEvents, setError, or setLoading(false) for
aborted/outdated requests; ensure the finally block also skips setLoading when
the request was aborted. Use the function name fetchEvents and state setters
setEvents, setError, setLoading to locate the changes.
| if (res.status === 403) { | ||
| const remaining = | ||
| res.headers.get("X-RateLimit-Remaining") || "0"; | ||
|
|
||
| const reset = | ||
| res.headers.get("X-RateLimit-Reset"); | ||
|
|
||
| const resetTime = reset | ||
| ? new Date(Number(reset) * 1000).toLocaleTimeString() | ||
| : "Unknown"; | ||
|
|
||
| setError( | ||
| `GitHub API rate limit exceeded. | ||
| Please try again after ${resetTime}. | ||
| Remaining Requests: ${remaining}` | ||
| ); | ||
|
|
||
| setEvents([]); | ||
| setLoading(false); | ||
| return; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the relevant section with context
sed -n '1,220p' src/components/ActivityFeed.tsx | nl -ba | sed -n '1,220p' | cat
# Narrow search for fetch/useEffect and error handling
rg -n "useEffect|fetch\\(|res\\.status|403|X-RateLimit-Remaining|X-RateLimit-Reset|setError\\(" src/components/ActivityFeed.tsx
# Print a tighter window around the snippet area
sed -n '1,140p' src/components/ActivityFeed.tsx | nl -ba | sed -n '20,120p' | catRepository: GitMetricsLab/github_tracker
Length of output: 115
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Basic sanity: file exists?
ls -la src/components/ActivityFeed.tsx
# Show the file with line numbers (full file may be small; otherwise window it)
wc -l src/components/ActivityFeed.tsx
# Print the likely region
sed -n '1,220p' src/components/ActivityFeed.tsx | cat -n
# Focused grep for relevant patterns
rg -n "useEffect|fetch\\(|res\\.status|\\b403\\b|X-RateLimit-Remaining|X-RateLimit-Reset|setError\\(" src/components/ActivityFeed.tsx
# Print a tighter window around the 403 handling by searching for the snippet
line=$(rg -n "\\bres\\.status\\s*===\\s*403\\b" src/components/ActivityFeed.tsx | cut -d: -f1 | head -n1 || true)
if [ -n "${line}" ]; then
start=$((line-30))
end=$((line+60))
if [ "$start" -lt 1 ]; then start=1; fi
sed -n "${start},${end}p" src/components/ActivityFeed.tsx | cat -n
fi
# Check if there's any abort controller or cancellation logic
rg -n "AbortController|abort\\(|signal\\b|cleanup\\b|return \\(\\) =>|didCancel|isMounted|requestId" src/components/ActivityFeed.tsxRepository: GitMetricsLab/github_tracker
Length of output: 8251
Fix incorrect 403 classification and stale fetch state in ActivityFeed
res.status === 403is currently treated as a GitHub rate-limit error unconditionally (andX-RateLimit-Remainingdefaults to"0"if missing), which will mislabel other 403 responses. Gate the rate-limit message on rate-limit headers instead (e.g.,X-RateLimit-Remaining === "0"), otherwise show a generic forbidden/error.
Suggested fix
- if (res.status === 403) {
- const remaining =
- res.headers.get("X-RateLimit-Remaining") || "0";
+ if (res.status === 403) {
+ const remaining = res.headers.get("X-RateLimit-Remaining");
const reset =
res.headers.get("X-RateLimit-Reset");
+ const isRateLimited = remaining === "0";
- const resetTime = reset
- ? new Date(Number(reset) * 1000).toLocaleTimeString()
- : "Unknown";
-
- setError(
- `GitHub API rate limit exceeded.
- Please try again after ${resetTime}.
- Remaining Requests: ${remaining}`
- );
+ if (isRateLimited) {
+ const resetTime = reset
+ ? new Date(Number(reset) * 1000).toLocaleTimeString()
+ : "Unknown";
+ setError(
+ `GitHub API rate limit exceeded. Please try again after ${resetTime}. Remaining Requests: ${remaining}`
+ );
+ } else {
+ setError("Access forbidden while fetching GitHub activity.");
+ }
setEvents([]);
setLoading(false);
return;
}-
useEffectusessetInterval(fetchEvents, 30000)and triggersfetchEvents()again without any request cancellation/in-flight guard; overlapping or in-flight responses can overwrite newer state whenusernamechanges or when calls race. Add anAbortController/signaland/or a request-id guard before callingsetEvents/setError/setLoading. -
Minor: the rate-limit message is a multiline template literal; simplify it to a single-line string to avoid awkward whitespace in the rendered text.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (res.status === 403) { | |
| const remaining = | |
| res.headers.get("X-RateLimit-Remaining") || "0"; | |
| const reset = | |
| res.headers.get("X-RateLimit-Reset"); | |
| const resetTime = reset | |
| ? new Date(Number(reset) * 1000).toLocaleTimeString() | |
| : "Unknown"; | |
| setError( | |
| `GitHub API rate limit exceeded. | |
| Please try again after ${resetTime}. | |
| Remaining Requests: ${remaining}` | |
| ); | |
| setEvents([]); | |
| setLoading(false); | |
| return; | |
| } | |
| if (res.status === 403) { | |
| const remaining = res.headers.get("X-RateLimit-Remaining"); | |
| const reset = | |
| res.headers.get("X-RateLimit-Reset"); | |
| const isRateLimited = remaining === "0"; | |
| if (isRateLimited) { | |
| const resetTime = reset | |
| ? new Date(Number(reset) * 1000).toLocaleTimeString() | |
| : "Unknown"; | |
| setError( | |
| `GitHub API rate limit exceeded. Please try again after ${resetTime}. Remaining Requests: ${remaining}` | |
| ); | |
| } else { | |
| setError("Access forbidden while fetching GitHub activity."); | |
| } | |
| setEvents([]); | |
| setLoading(false); | |
| return; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/ActivityFeed.tsx` around lines 40 - 60, The code treats any
403 as a rate-limit and can let stale responses overwrite state; update the
fetchEvents logic in ActivityFeed to: 1) detect rate-limit only when rate-limit
headers indicate it (e.g., const remaining =
res.headers.get("X-RateLimit-Remaining"); if (remaining === "0") { const reset =
res.headers.get("X-RateLimit-Reset"); const resetTime = reset ? new
Date(Number(reset) * 1000).toLocaleTimeString() : "Unknown"; setError(`GitHub
API rate limit exceeded. Please try again after ${resetTime}. Remaining
Requests: ${remaining}`); } else if (res.status === 403) { setError("Access
forbidden"); } 2) introduce an AbortController and/or a monotonically increasing
requestId inside useEffect around fetchEvents, pass signal to fetch, abort on
cleanup, and ignore responses whose requestId is stale before calling
setEvents/setError/setLoading; and 3) make the rate-limit message a single-line
string to avoid whitespace issues.
| setError( | ||
| `GitHub API rate limit exceeded. | ||
| Please try again after ${resetTime}. | ||
| Remaining Requests: ${remaining}` | ||
| ); |
There was a problem hiding this comment.
Error string formatting includes unintended whitespace/newlines.
The multiline template literal will render with extra spacing. Use a single-line template string for cleaner alert text.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/ActivityFeed.tsx` around lines 51 - 55, The error message
passed to setError in ActivityFeed.tsx uses a multiline template literal which
introduces unintended whitespace and newlines; replace it with a single-line
interpolated string (e.g. `GitHub API rate limit exceeded. Please try again
after ${resetTime}. Remaining Requests: ${remaining}`) or build the message via
concatenation/trim to eliminate extra spacing before calling setError; update
the invocation where setError(...) is used so the alert text is a compact
single-line string.
|
🎉🎉 Thank you for your contribution! Your PR #533 has been merged! 🎉🎉 |
Related Issue
Description
Implemented GitHub API rate limit handling in the Activity Feed component.
Changes Made
403)X-RateLimit-ResetX-RateLimit-RemainingHow Has This Been Tested?
Screenshots (if applicable)
Type of Change
Summary by CodeRabbit
Bug Fixes
UI/Style