What's wrong
On /leaderboard, the Longest Visit Streak → Active card contains two entries for the same user (id = 679b590f31e842a9fc848267, name Lost) with different streak values — 158 and 151. Toggling Active ↔ Lifetime tabs causes additional <li> elements for this user to accumulate in the DOM — after 3 toggles the Active list already shows three Lost — 158 rows.
Steps to reproduce
- Open https://roadmap.sh/leaderboard
- The «Longest Visit Streak» card opens on the Active tab by default — note the two
Lost rows (158 and 151)
- Click
Lifetime → then Active → repeat 2–3 times
- Duplicates of
Lost — 158 start appearing in the Active list, and similarly in Lifetime; both cards visibly grow
Evidence — API response
GET https://api.roadmap.sh/v1-list-leaderboard-stats → streaks.active:
[
{ "id": "6712dee6791f57dd60d309cd", "name": "IgorLutiy", "count": 379 },
{ "id": "64f6acfa5ce9f4ca589060c4", "name": "samuel", "count": 280 },
...
{ "id": "679b590f31e842a9fc848267", "name": "Lost", "count": 158 },
{ "id": "6761b41e8fe51199dac494b4", "name": "Brofy", "count": 152 },
{ "id": "679b590f31e842a9fc848267", "name": "Lost", "count": 151 }
]
The same id appears twice in the array with different count values.
Root cause
Backend: the leaderboard aggregation does not deduplicate entries by user_id — a single user can land in streaks.active more than once (likely two stored streak sessions for the same account being concatenated without GROUP BY user_id / DISTINCT).
Frontend (amplifies the bug): src/components/Leaderboard/LeaderboardPage.tsx:172 uses <li key={user.id}>. When the API returns duplicate ids, React hits a key collision within a single list. Reconciliation on tab toggle becomes undefined and stale DOM nodes are not unmounted, so each Active ↔ Lifetime toggle adds another row.
Suggested fixes
- Primary — backend: guarantee
user_id uniqueness in every leaderboard array (active, lifetime, and friends). E.g. DISTINCT ON (user_id) ORDER BY count DESC (Postgres) or the equivalent aggregation step.
- Defense on the frontend — change the key in
LeaderboardPage.tsx:172:
- key={user.id}
+ key={\`\${user.id}-\${counter}\`}
This removes the reconciliation UB even if the backend ever regresses to returning duplicates again.
Environment
What's wrong
On
/leaderboard, the Longest Visit Streak → Active card contains two entries for the same user (id = 679b590f31e842a9fc848267, nameLost) with different streak values — 158 and 151. Toggling Active ↔ Lifetime tabs causes additional<li>elements for this user to accumulate in the DOM — after 3 toggles the Active list already shows threeLost — 158rows.Steps to reproduce
Lostrows (158 and 151)Lifetime→ thenActive→ repeat 2–3 timesLost — 158start appearing in the Active list, and similarly in Lifetime; both cards visibly growEvidence — API response
GET https://api.roadmap.sh/v1-list-leaderboard-stats→streaks.active:[ { "id": "6712dee6791f57dd60d309cd", "name": "IgorLutiy", "count": 379 }, { "id": "64f6acfa5ce9f4ca589060c4", "name": "samuel", "count": 280 }, ... { "id": "679b590f31e842a9fc848267", "name": "Lost", "count": 158 }, { "id": "6761b41e8fe51199dac494b4", "name": "Brofy", "count": 152 }, { "id": "679b590f31e842a9fc848267", "name": "Lost", "count": 151 } ]The same
idappears twice in the array with differentcountvalues.Root cause
Backend: the leaderboard aggregation does not deduplicate entries by
user_id— a single user can land instreaks.activemore than once (likely two stored streak sessions for the same account being concatenated withoutGROUP BY user_id/DISTINCT).Frontend (amplifies the bug):
src/components/Leaderboard/LeaderboardPage.tsx:172uses<li key={user.id}>. When the API returns duplicateids, React hits a key collision within a single list. Reconciliation on tab toggle becomes undefined and stale DOM nodes are not unmounted, so each Active ↔ Lifetime toggle adds another row.Suggested fixes
user_iduniqueness in every leaderboard array (active,lifetime, and friends). E.g.DISTINCT ON (user_id) ORDER BY count DESC(Postgres) or the equivalent aggregation step.LeaderboardPage.tsx:172:Environment