windmill-labs/windmill · error
GET /assets/graph → ${res.status}
Error message
GET /assets/graph → ${res.status} What it means
The pipeline graph loader fetches GET /w/{ws}/assets/graph and, on any non-OK HTTP response, throws this error embedding the status code. It is the app's own guard so the failed fetch surfaces instead of a confusing JSON parse error on an error body.
Source
Thrown at frontend/src/routes/(root)/(logged)/pipeline/[folder]/+page.svelte:2238
if (g.runnables.length === 0 && g.assets.length === 0) {
setMode('edit', { replace: true })
}
})
let graphRes = resource(
[() => $workspaceStore, () => folder],
async ([ws, f], _prev, { signal }) => {
if (!ws || !f) return undefined
const base_url = OpenAPI.BASE ?? ''
const params = new URLSearchParams({
folder: f,
asset_kinds: DATA_KINDS.join(',')
})
const res = await fetch(`${base_url}/w/${ws}/assets/graph?${params}`, {
credentials: 'include',
signal
})
if (!res.ok) throw new Error(`GET /assets/graph → ${res.status}`)
return hideDbtRunnables((await res.json()) as AssetGraphResponse)
}
)
// Folder whose graph is actually rendered. `graphRes.current` is stale-
// while-revalidate on an in-place folder switch, so keying the canvas's
// one-shot initial fit on the route param would fire the new folder's fit
// on the old graph and leave the fresh one unfitted. `folder` is read
// untracked: the key must move only when a graph lands.
let viewportFitFolder = $state('')
$effect(() => {
if (graphRes.current) untrack(() => (viewportFitFolder = folder))
})
// Body / inferred-assets prefetch sweep. Watches `g.runnables`; for any
// non-draft path we haven't fetched yet, fetches `getScriptByPath` and
// `inferAssets`, and stores both in their respective only-add caches.
// All three previously-sticky maps (`inferredWritesByPath`,View on GitHub (pinned to e474e8803c)
Solutions
- Read the status in the message: 401 → re-login, 403 → request workspace access, 500 → check backend logs for the graph endpoint
- Refresh the page / re-authenticate and retry the fetch
- Check backend logs around /assets/graph for graph-computation failures
- Reduce graph size (filter folders/kinds) if the backend times out on huge graphs
Example fix
// before
if (!res.ok) throw new Error(`GET /assets/graph → ${res.status}`)
// after
if (!res.ok) {
const body = await res.text().catch(() => '')
throw new Error(`GET /assets/graph → ${res.status}: ${body.slice(0, 200)}`)
} Defensive patterns
Strategy: try-catch
Validate before calling
const res = await fetch(url, { credentials: 'include', signal })
if (!res.ok) {
if (res.status === 401) await reLogin()
if (res.status === 403) throw new Error('Missing workspace access for /assets/graph')
} Type guard
function isOk(res: Response): res is Response & { ok: true } { return res.ok } Try / catch
try {
const graph = await loadAssetGraph(ws, signal)
} catch (e) {
const m = /→ (\d{3})/.exec(e.message)
if (m?.[1] === '401') redirectToLogin()
else sendUserToast(`Failed to load asset graph${m ? ` (HTTP ${m[1]})` : ''}`, true)
} Prevention
- Keep the session alive on long-lived pipeline tabs (refresh token or re-login prompt on 401)
- Check workspace permissions before rendering graph-dependent UI
- Log the response body on non-OK to distinguish auth vs server errors
- Use AbortSignal properly so canceled polls aren't misread as server failures
When it happens
Trigger: Any request to /assets/graph (with asset_kinds=DATA_KINDS) that returns 4xx/5xx: expired session cookie, missing permissions on the workspace, malformed query params, or backend error while computing the dependency graph.
Common situations: Session expired while the pipeline tab was open (401); user lacks access to the workspace/folder (403); backend timeout or DB error computing a large asset graph (500); reverse proxy returning 502.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- HTTP ${res.status} ${res.statusText}
- ${response.status} ${text}
- ApiError with mapped HTTP status message (e.g. "Not Found",
- Generic Error: status: ${errorStatus}; status text: ${errorS
- body.error || res.statusText
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/a1f801647ba2e4de.
Report an issue: GitHub.