Symptom
A charging session stayed stuck in “active” status even after the CDR callback was received. Only affected specific sessions intermittently.
Root cause
The polling function fetched one page of sessions and returned it — never checking for more. VoltNet’s default page limit is 20. When >20 sessions were updated in a 30-second window, any session ranked #21+ was silently truncated. If that was the session’s final status update, it was missed permanently.
// ❌ Assumes all sessions fit in one response
const res = await axios.get(url)
return res.data?.sessions || []This bug was silent — no errors, worked fine under low volume, only manifested under concurrent load.
Fix: pagination loop
while (hasMorePages) {
const res = await axios.get(`${url}&offset=${offset}`)
const sessions = res.data?.sessions || []
allSessions.push(...sessions)
if (sessions.length < PAGE_SIZE) {
hasMorePages = false
} else {
offset += PAGE_SIZE
}
if (offset >= 2000) { // safety limit — prevent infinite loops
logger.error('Pagination safety limit reached')
break
}
}Lessons
- Never assume complete results from a paginated API — if result count equals the limit, there may be more pages
- Silent failures are the worst — no errors, fails only under specific load conditions, invisible in testing
- Always add a pagination safety limit to prevent infinite loops
- Test at page boundaries — test with exactly 20, 21, 40, 41 records to catch truncation bugs
There was also a secondary issue: callbacks were being sent to v0.2.0 instead of v0.3.0, causing 400 errors. Fixed by updating the callback URL config. Callbacks should be primary; polling is the fallback.