fix(dashboard): defensive guards prevent 'is not iterable' on partial backend responses (closes #304) by cristim · Pull Request #311 · LeanerCloud/cloud-commitments-cli · GitHub
Skip to content

fix(dashboard): defensive guards prevent 'is not iterable' on partial backend responses (closes #304) - #311

Merged
cristim merged 1 commit into
feat/multicloud-web-frontendfrom
fix/issue-304-dashboard-iterable
May 5, 2026
Merged

cristim merged 1 commit into
feat/multicloud-web-frontendfrom
fix/issue-304-dashboard-iterable

Conversation

@cristim

@cristim cristim commented May 5, 2026 •

Copy link
Copy Markdown
Member

Summary

  • getRecommendations() can resolve to null (when apiRequest's JSON-parse catch fires on a 2xx with empty/non-JSON body) or an unexpected object shape — both reach groupRecsByCell() which uses for...of, throwing "X is not iterable" and replacing the entire dashboard with an error banner.
  • Added Array.isArray() guard in loadDashboard() to coerce any non-array fulfilled value to [] before it reaches groupRecsByCell().
  • Added try/catch soft-fail in renderDashboardSummary() around the savings-range computation so the Potential Monthly Savings card degrades to $0 rather than blanking the dashboard if the guard is ever bypassed.

Fault site (PR #295 — commit e4a52f9)

// Before: no guard — null/object from apiRequest lands directly in groupRecsByCell
const recs = recsResult.status === 'fulfilled'
  ? (recsResult.value as unknown as LocalRecommendation[])
  : [];

Regression tests added

  • #304: getRecommendations returning null does not throw "is not iterable"
  • #304: getRecommendations returning a non-array object does not throw "is not iterable"
  • #304: summaryData missing by_service field does not throw

All 24 dashboard tests pass.

Test plan

  • Run cd frontend && npm test -- --testPathPattern=dashboard — all 24 tests pass
  • Verify the three new #304 tests are green
  • Manually test with a backend returning 5xx for /recommendations — other cards still render, savings card shows $0

Closes #304

Summary by CodeRabbit

Bug Fixes

  • Dashboard now gracefully handles unexpected recommendation data formats, preventing application crashes when the API returns null or other malformed response data
  • The savings calculation automatically falls back to displaying $0 when encountering unexpected data structures, instead of causing the interface to crash
  • Dashboard rendering continues to work correctly even when summary data is missing optional fields

… backend responses (closes #304)

The 'Failed to load dashboard: e is not iterable' error was triggered when
api.getRecommendations() resolved to a non-array value. The apiRequest()
catch block returns `null as T` when response.json() fails on a 2xx response
with an empty or non-JSON body; a non-array envelope shape from the backend
produces the same symptom. Both land in groupRecsByCell() which iterates via
`for...of`, throwing "null/X is not iterable" and replacing the dashboard
with an error banner.

Two layered guards added to dashboard.ts:
- Array.isArray() check in loadDashboard() coerces any non-array recsResult
  value to [] before it reaches groupRecsByCell().
- try/catch soft-fail in renderDashboardSummary() around the
  groupRecsByCell/pageLevelRange computation so the savings card degrades
  to $0 rather than blanking the entire dashboard if the guard above is
  ever bypassed.

Regression tests added for:
  (a) getRecommendations() returning null
  (b) getRecommendations() returning a non-array object ({recommendations:[]})
  (c) summaryData missing by_service field entirely
@coderabbitai

coderabbitai Bot commented May 5, 2026 •

Copy link
Copy Markdown
Contributor

@cristim cristim added priority/p1 Next up; this sprint severity/high Significant harm urgency/this-sprint Within the current sprint impact/all-users Affects every user effort/s Hours type/bug Defect triaged Item has been triaged labels May 5, 2026
@cristim

cristim commented May 5, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/all-users Affects every user priority/p1 Next up; this sprint severity/high Significant harm triaged Item has been triaged type/bug Defect urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant