Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneesstack72

Relationships

#1956 Dashboard renders a blank page when any workflow run has a pending manual approval (workflow.approvals shape mismatch)

Opened by lightning_swamp · 9/2/2026· Shipped 9/2/2026

Version: 20260902.023341.0-sha.820f22f0 (also reproduced on 20260901.013347.0-sha.58c65184; both ship the byte-identical dashboard bundle index-CV5pVcca.js, so swamp update does not fix it) Platform: Linux (WSL 1), Chrome 152, --auth-mode none --dashboard

Summary

The bundled web dashboard renders an empty page as soon as at least one workflow run is suspended on a manual_approval step. The nav shell paints for a moment, then the entire React tree unmounts and the page goes blank (black, on the default dark theme).

With zero pending approvals the dashboard renders perfectly. So the dashboard is unusable for exactly the case it is most needed: an approval-gated runbook.

Reproduction

  1. Create a workflow with a manual_approval step.
  2. Start a run and let it suspend at the gate.
  3. Start swamp serve --dashboard and open /dashboard.

Actual behaviour

Uncaught exception, #root left empty:

TypeError: Cannot read properties of undefined (reading 'length')
    at index-CV5pVcca.js:50:17821
    at Array.reduce (<anonymous>)
    at tg (index-CV5pVcca.js:50:17795)
    at Sc (index-CV5pVcca.js:48:47958)

Captured via CDP; document.getElementById('root').innerHTML is ''. No failed network requests.

Root cause

workflow.approvals returns flat entries, one per suspended step:

{"data":{"approvals":[
  {"workflowName":"mvp-runbook","runId":"53314a32-...","stepName":"approve-step-2",
   "suspendedAt":"2026-09-02T14:03:02.743Z","prompt":"Approve to run MIG002?",
   "inputs":{"app_name":"DemoApp","wave_name":"Wave01"}}
]}}

The Overview component expects them grouped, with a nested steps array:

Site Code Problem
count badge N.reduce((G,te)=>G+te.steps.length,0) throws — te.steps is undefined
list render N.flatMap(G=>G.steps.map(te=>...)) would throw for the same reason
approve btn T(G.workflowName,te.name) te.name is undefined; the field is stepName
reject btn B(G.workflowName,te.name) same
react key `${G.runId}-${te.name}` same

The sidebar badge in jp guards with Array.isArray(q.steps) so it does not throw, but it therefore always displays 0 pending approvals.

Suggested fix

Pick one side of the contract:

  • have workflow.approvals return grouped entries — {workflowName, runId, steps:[{name, prompt}]} — which is what the UI already assumes; or
  • have the dashboard read the flat shape (stepName, one entry per step).

Independently, an error boundary around the page components would stop a single unexpected payload from blanking the entire app — a wrong approval count is a much better failure mode than a blank screen with no visible error.

serve.config is sent by the dashboard bundle but is not in the server's accepted command discriminator list, so it always errors. The list returned from an invalid-type probe contains 104 commands (server.version, workflow.run, workflow.approvals, ...) but no serve.config. This does not cause the blank page, but suggests the bundled dashboard and the server command set are out of sync.

Workaround

Reverse-proxying /dashboard/assets/index-*.js and rewriting those five expressions to read the flat shape makes the dashboard render correctly, with the approval count, list, and working Approve/Reject buttons.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 2 MOREREVIEW+ 7 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

9/2/2026, 3:51:28 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack729/2/2026, 3:28:28 PM
Editable. Press Enter to edit.

stack72 commented 9/2/2026, 3:51:40 PM

Thanks @lightning_swamp for reporting this! The fix has been merged and a release is on its way. We appreciate your contribution to swamp.

Sign in to post a ripple.