{"record":{"id":"d8801e8e4567f905","repo":"windmill-labs/windmill","slug":"missing-context-for-draft-fetch","errorCode":null,"errorMessage":"Missing context for draft fetch","messagePattern":"Missing context for draft fetch","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"frontend/src/lib/components/DraftBadge.svelte","lineNumber":149,"sourceCode":"\t)\n\n\tlet busyFor = $state<string | null>(null)\n\tlet diffDrawer: DiffDrawer | undefined = $state(undefined)\n\tlet migrateOpen = $state(false)\n\t// The hover popover sits above the diff drawer / migrate modal (z-index), so\n\t// close it before opening either or it would cover them.\n\tlet popoverOpen = $state(false)\n\n\t// Legacy (no-owner) drafts can only be resolved by workspace admins / superadmins.\n\tconst canMigrateLegacy = $derived(!!$userStore?.is_admin || !!$userStore?.is_super_admin)\n\n\tfunction ownerKey(owner: DraftUser): string {\n\t\treturn owner.username ?? '__legacy__'\n\t}\n\n\tasync function fetchDraft(owner: DraftUser): Promise<unknown> {\n\t\tif (!workspace || !itemKind || !path) {\n\t\t\tthrow new Error('Missing context for draft fetch')\n\t\t}\n\t\treturn (\n\t\t\tawait DraftService.getDraftForUser({\n\t\t\t\tworkspace,\n\t\t\t\tkind: itemKind,\n\t\t\t\tpath,\n\t\t\t\tusername: owner.username ?? undefined\n\t\t\t})\n\t\t).value\n\t}\n\n\tasync function viewDiff(owner: DraftUser) {\n\t\tif (!workspace || !itemKind || !path) return\n\t\tbusyFor = ownerKey(owner)\n\t\ttry {\n\t\t\tconst [draftValue, deployed] = await Promise.all([\n\t\t\t\tfetchDraft(owner),\n\t\t\t\tfetchDeployedValueForDiff(workspace, itemKind, path)","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/DraftBadge.svelte#L131-L167","documentation":"In frontend/src/lib/components/DraftBadge.svelte:149, fetchDraft() requires workspace, itemKind, and path to call DraftService.getDraftForUser; if any is missing it throws \"Missing context for draft fetch\". This guards against querying drafts for an item that is not (yet) identified.","triggerScenarios":"DraftBadge rendered for an unsaved/new item with no path, or outside a workspace context — e.g. the component mounts before the item's path is assigned.","commonSituations":"Creating a brand-new script/flow and opening the draft badge before first save; component rendered in a context without workspace selection; a race where path prop is briefly empty on mount.","solutions":["Save the item first so it has a path before draft lookups","Ensure the component is only rendered when workspace and path are set (use {#key path} or a guard)","Check where itemKind/path are sourced and fix the empty value"],"exampleFix":"// before\n{#if showDraft}<DraftBadge ... />{/if}\n// after\n{#if showDraft && path}<DraftBadge ... />{/if}","handlingStrategy":"type-guard","validationCode":"if (!workspace || !itemKind || !path) return; // skip draft fetch until item is identified","typeGuard":"function canFetchDraft(ctx: { workspace?: string; itemKind?: string; path?: string }): ctx is { workspace: string; itemKind: string; path: string } {\n  return Boolean(ctx.workspace && ctx.itemKind && ctx.path);\n}","tryCatchPattern":"try {\n  draft = await fetchDraft(owner);\n} catch {\n  draft = undefined; // no draft yet or missing context — treat as no draft\n}","preventionTips":["Render DraftBadge only when the item has a saved path","Gate draft fetches behind a reactive check of workspace/itemKind/path","Key the component with {#key path} so state resets when the path changes"],"tags":["drafts","frontend","missing-context"],"backgroundTag":"missing-required-context","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}