windmill-labs/windmill · error · Error

GET assets/graph -> ${res.status}: ${await res.text()}

Error message

GET assets/graph -> ${res.status}: ${await res.text()}

What it means

Thrown by fetchDeployedGraph in the Windmill CLI pipeline docs tooling when the raw GET to `/w/{workspaceId}/assets/graph` returns a non-OK HTTP status. The error message includes the status code and the response body so the server-side reason (auth, bad folder, unsupported asset_kinds parameter) is surfaced directly.

Source

Thrown at cli/src/commands/pipeline/docs.ts:41

} from "./localGraph.ts";

const ASSET_KINDS = "s3object,ducklake,datatable,volume,dbt";

function assetUri(kind: string, p: string): string {
  const prefix = kind === "s3object" ? "s3" : kind;
  return `${prefix}://${p}`;
}

async function fetchDeployedGraph(
  workspaceId: string,
  folder: string,
): Promise<AssetGraph> {
  const res = await fetch(
    `${OpenAPI.BASE}/w/${workspaceId}/assets/graph?folder=${encodeURIComponent(folder)}&asset_kinds=${ASSET_KINDS}`,
    { headers: { Authorization: `Bearer ${OpenAPI.TOKEN}` } },
  );
  if (!res.ok) {
    throw new Error(`GET assets/graph -> ${res.status}: ${await res.text()}`);
  }
  return hideDbtRunnables((await res.json()) as AssetGraph);
}

// Render the pipeline graph as a markdown document.
export function generatePipelineMarkdown(
  folder: string,
  graph: AssetGraph,
  datatableSchemas: any[],
  local: boolean,
): string {
  const writesByScript = new Map<string, string[]>();
  const readsByScript = new Map<string, string[]>();
  for (const e of graph.edges) {
    if (e.runnable_kind !== "script") continue;
    const uri = assetUri(e.asset_kind, e.asset_path);
    if (e.access_type === "w" || e.access_type === "rw") {
      (writesByScript.get(e.runnable_path) ?? writesByScript.set(e.runnable_path, []).get(e.runnable_path)!).push(uri);

View on GitHub (pinned to e474e8803c)

Solutions

  1. Check the status code and body in the message: 401/403 -> re-authenticate (`wmill auth login` or refresh WMILL_TOKEN).
  2. 404 -> verify the folder name and confirm the server version supports the /assets/graph endpoint; upgrade the server if it predates the feature.
  3. 5xx -> retry later or check the server logs; if behind a proxy, test the same request with curl to isolate it.
  4. Confirm OpenAPI.BASE points at the intended instance (check --base-url / WMILL_BASE_URL).

Example fix

// before: stale token
export WMILL_TOKEN=eyJhbGciOi...expired

// after
wmill auth login --token <fresh-token> --workspace <ws>
Defensive patterns

Strategy: retry

Validate before calling

const base = OpenAPI.BASE;
if (!base) throw new Error("Set WMILL_BASE_URL / run `wmill auth login` before fetching the graph");

Try / catch

try {
  const graph = await fetchDeployedGraph(workspaceId, folder);
} catch (e: any) {
  const m = /-> (\d{3}):/.exec(e.message);
  if (m && ["502", "503", "504"].includes(m[1])) {
    await sleep(2000); // transient server/proxy error — retry
  } else if (m && (m[1] === "401" || m[1] === "403")) {
    console.error("Token expired or unauthorized — run `wmill auth login`");
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `wmill pipeline docs/graph` against a workspace whose backend does not expose the assets/graph endpoint (older server), with an invalid or expired bearer token (401), a folder that does not exist, or a transient 5xx from the server.

Common situations: CLI version newer than the connected Windmill server (endpoint not yet deployed); WMILL_TOKEN expired or scoped to a different workspace; a typo'd/nonexistent --folder; corporate proxy returning 502/503.

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


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/d1865d23c15ad4dd. Report an issue: GitHub.