bountyPoller calls fetchBounty(id) with no fallback argument: wrong arity, and a backend outage silently blanks the bounty instead of surfacing an error
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 65/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- next.js, react, typescript
- Domain
- backend-api-design, frontend
Research direction
Read src/hooks/bountyPoller.ts alongside the fetchBounty declaration in src/lib/api.ts and the caller in src/hooks/useClaimRace.ts. First run npx tsc --noEmit to confirm the arity error, then trace how a failed poll updates bounty and error state. Done when the poller typechecks, a failed refresh preserves the last known bounty and exposes an error, and the successful polling path still updates the status.
Written by the indexing model from the issue text.
Description
Location
src/hooks/bountyPoller.ts:45 inside startPolling's tick, against fetchBounty as declared in src/lib/api.ts (export const fetchBounty = cache(async (id: string, fallback: Bounty | undefined) => ...)).
Problem
Two related defects at the same call site.
1. Wrong arity. fetchBounty takes two required parameters. The poller calls it with one:
const result = await fetchBounty(id);
fallback is typed Bounty | undefined but is not optional (no ?), so this is a TS2554: Expected 2 arguments, but got 1 error. The sibling caller gets it right (src/hooks/useClaimRace.ts:77 passes fetchBounty(bountyId, undefined)), which is why the mistake is isolated to this file.
2. The error branch is unreachable, and failures look like "bounty does not exist". fetchBounty never throws — it is a fallback-style fetcher that swallows everything:
try { ... return { data: adaptBounty(raw), source: "live" }; }
catch { return { data: fallback, source: "mock" }; }
With fallback undefined, a backend error/timeout resolves to { data: undefined, source: "mock" }, and the poller then does:
entry.bounty = result.data ?? null; // -> null
entry.source = result.source;
entry.error = null; // explicitly cleared
So entry.error can never be set by the normal path (the catch (err) block at :54-56 is dead code for the same reason #240 was dead code), and a transient outage overwrites an already-loaded bounty with null. useBountyStatus then returns bounty: null, error: null, isLoading: false — indistinguishable from "this bounty id does not exist" — and any consumer rendering bounty ? ... : <NotFound/> flips a populated status card to an empty/not-found state on a single failed poll, then back again on the next successful one.
Why it matters
- The project does not typecheck (see the arity error), on top of whatever other build problems exist.
- During a backend blip the bounty page's status area blanks out mid-session rather than keeping the last known value or showing "couldn't refresh". For a user mid-claim, a bounty that appears to vanish and reappear is both alarming and actionable in the wrong direction.
- Because
erroris permanentlynull, every error-handling branch downstream ofuseBountyStatus().erroris unreachable, so nobody can see that polling is failing at all.
Reproduction
- Open
/issues/<id>against a running backend and let the status populate. - Stop the backend (or block
/bounties/:idat the network level). - Within 5s the poller ticks,
fetchBountyfalls back, andentry.bountybecomesnullwhileentry.errorstaysnull. - The status area renders as if the bounty does not exist; no error state, no retry message.
For the arity error: npx tsc --noEmit.
Suggested fix
Have the poller use a fetcher that reports failure instead of masking it. Either call the raw request<RawBounty> helper (exported for this kind of use) and adapt, or add a throwing variant to src/lib/api.ts and call it here. Then:
const raw = await fetchBountyOrThrow(id); // throws on network/HTTP/parse failure
entry.bounty = raw;
entry.error = null;
and in the catch, set entry.error without clobbering entry.bounty — keep the last known good value so the UI can show "last updated Xs ago, retrying" rather than an empty card. If the fallback-style fetcher is kept for now, at minimum pass the second argument explicitly and treat source === "mock" as the failure signal (entry.error = new Error(...), bounty left untouched) instead of writing null.
- Dominant language
- TypeScript
- Stars
- 1
- Forks
- 94
- Avg merge
- 1h 2m
- Merged PRs (30d)
- 25
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from MergeFi/frontend
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
bug testing
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Maintainers usually reply within 1 day
-
accessibility bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
MergeFi/frontend#835 · 1 comment ·
Maintainers usually reply within 1 day
All issues in MergeFi/frontend
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
zapcooking/frontend#946 ·
Maintainers usually reply within 1 day
-
enhancement good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
WiliamRosa/cosmos-semantic-platform#40 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
liberation-data/drivine#733 · 1 comment ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 64/100
makecindy/cindy#10062 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day