garrytan/gstack · error · Error
diagram-render bundle did not become ready in the browse tab
Error message
diagram-render bundle did not become ready in the browse tab (${READY_TIMEOUT_MS}ms). Check `browse js "window.__errors"` on tab ${this.tabId}. What it means
Thrown by RenderTab.loadBundle() when the diagram-render bundle page does not expose a #status element reading 'ready' within READY_TIMEOUT_MS after loadHtmlFile. The bundle is the headless renderer for mermaid/excalidraw; if it never initializes, no fence can render. The message points at `window.__errors` on the tab for diagnostics.
Source
Thrown at make-pdf/src/diagram-prepass.ts:367
if (!survivorOk) throw renameErr;
}
}
const tabId = browseClient.newtab();
const tab = new RenderTab(tabId, staged);
tab.loadBundle();
return tab;
}
/** (Re)load the bundle page — also the reset path after a render error. */
loadBundle(): void {
browseClient.loadHtmlFile({ file: this.stagedBundlePath, tabId: this.tabId });
const ready = browseClient.waitForExpression({
expression: "document.getElementById('status') !== null && document.getElementById('status').textContent === 'ready'",
tabId: this.tabId,
timeoutMs: READY_TIMEOUT_MS,
});
if (!ready) {
throw new Error(
"diagram-render bundle did not become ready in the browse tab " +
`(${READY_TIMEOUT_MS}ms). Check \`browse js "window.__errors"\` on tab ${this.tabId}.`,
);
}
}
/**
* Call one of the bundle's async window functions with JSON-safe string
* args. Errors come back as a recognizable ERR: prefix so a render failure
* is data, not a thrown browse exit.
*/
call(fn: string, ...args: Array<string | number>): string {
const argList = args.map((a) => JSON.stringify(a)).join(",");
const expression =
`window.${fn}(${argList})` +
`.then(r => "OK:" + r)` +
`.catch(e => "ERR:" + String((e && e.message) || e))`;
const result = this.js(expression);View on GitHub (pinned to 94993f7401)
Solutions
- Inspect bundle errors: `browse js "window.__errors"` on the tab id from the message.
- Rebuild the bundle: `bun run build:diagram-render` (repo) or re-run ./setup (install).
- Increase READY_TIMEOUT_MS if the daemon is cold and the machine is slow.
- Confirm the bundle HTML resolves to the right file (see resolveBundlePath error 329).
Example fix
// before
loadBundle(): void {
browseClient.loadHtmlFile({ file: this.stagedBundlePath, tabId: this.tabId });
const ready = browseClient.waitForExpression({ expression: "...status==='ready'", tabId: this.tabId, timeoutMs: READY_TIMEOUT_MS });
if (!ready) throw new Error(`diagram-render bundle did not become ready ...`);
}
// after: capture boot errors into the thrown message
loadBundle(): void {
browseClient.loadHtmlFile({ file: this.stagedBundlePath, tabId: this.tabId });
const ready = browseClient.waitForExpression({ expression: "...status==='ready'", tabId: this.tabId, timeoutMs: READY_TIMEOUT_MS });
if (!ready) {
const errs = browseClient.js({ expression: 'JSON.stringify(window.__errors ?? [])', tabId: this.tabId });
throw new Error(`diagram-render bundle did not become ready (${READY_TIMEOUT_MS}ms). Boot errors: ${errs}`);
}
} Defensive patterns
Strategy: retry
Try / catch
async function loadBundleWithRetry(tab: RenderTab, attempts = 2): Promise<void> {
for (let i = 0; i < attempts; i++) {
try {
tab.loadBundle();
return;
} catch (e) {
if (i === attempts - 1) throw e;
// reload the tab from scratch before retrying
await new Promise(r => setTimeout(r, 500));
}
}
} Prevention
- Rebuild the bundle (`bun run build:diagram-render`) after pulling the repo.
- Inspect window.__errors immediately when a loadBundle fails.
- Give the daemon a warm-up render before timing-sensitive runs.
- Keep READY_TIMEOUT_MS comfortably above the worst observed cold-start.
When it happens
Trigger: loadBundle() is called (initial load or the reset-after-error path) and waitForExpression times out. Causes: a bundle HTML file that is wrong/corrupt, a JS exception during bundle boot (visible in window.__errors), a wedged renderer, READY_TIMEOUT_MS too low for a cold Chromium, or a loadHtmlFile that silently failed.
Common situations: A stale diagram-render.html from an older build missing the status element; a CDN/runtime dependency the bundle expects is blocked (offline posture); Chromium sandbox failure on a fresh VM; the bundle was overwritten by a partial build; CJK or huge content exhausting memory at boot.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- ${bundleErrorMessage}
- 127
- browse ${args[0] || "unknown"} exited ${exitCode}: ${stderr}
- 1
- diagram-render bundle not found. Tried: ${candidates.map((c)
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/078eaea0909cd80a.
Report an issue: GitHub.