{"record":{"id":"e90ec53c982a2b4c","repo":"windmill-labs/windmill","slug":"no-draft-found-for-type-path","errorCode":null,"errorMessage":"No draft found for ${type} \"${path}\".","messagePattern":"No draft found for (.+?) \"(.+?)\"\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"frontend/src/lib/components/copilot/chat/global/core.ts","lineNumber":6151,"sourceCode":"\t\tresource: 'variable',\n\t\tpath\n\t}\n}\n\nasync function discardLocalDraft(\n\targs: { type: WorkspaceItemType; path: string; trigger_kind?: TriggerKind },\n\tctx: WriteDraftCtx\n): Promise<string> {\n\tconst { workspace, toolId, toolCallbacks } = ctx\n\tconst { type, path, trigger_kind: triggerKind } = args\n\n\tif (type === 'trigger' && !triggerKind) {\n\t\tthrow new Error('trigger_kind is required when discarding a trigger draft.')\n\t}\n\n\tconst draft = await getGlobalDraft(workspace, type, path, triggerKind)\n\tif (!draft) {\n\t\tthrow new Error(`No draft found for ${type} \"${path}\".`)\n\t}\n\n\tawait deleteGlobalDraft(workspace, type, path, triggerKind)\n\n\t// The chat's touch on the item is undone — drop it from the mask so a\n\t// pre-existing deployed item doesn't keep reading as this chat's edit.\n\tconst discardedKind = itemKindFor(type, triggerKind)\n\tif (discardedKind) {\n\t\ttoolCallbacks.onItemDiscarded?.(\n\t\t\tdiscardedKind,\n\t\t\tgetGlobalDraftStoragePath(workspace, type, path, triggerKind)\n\t\t)\n\t}\n\n\ttoolCallbacks.setToolStatus(toolId, {\n\t\tcontent: `Discarded ${type} \"${path}\" draft`,\n\t\tresult: 'Draft discarded'\n\t})","sourceCodeStart":6133,"sourceCodeEnd":6169,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/global/core.ts#L6133-L6169","documentation":"Thrown by discard_draft when getGlobalDraft returns no draft for the given type/path (and trigger_kind): there is nothing to discard. The tool refuses so the model knows its assumption of a pending draft is wrong.","triggerScenarios":"discard_draft is called when no draft exists — the item was never edited in this session, the draft was already discarded/deployed, or type/path/trigger_kind don't match the stored draft key.","commonSituations":"Double-discard after an earlier successful discard; deploying erased the draft so discard is unnecessary; wrong path casing or wrong trigger_kind so the lookup misses.","solutions":["Verify a draft exists first (read the item's draft status) before discarding","If the goal was to undo changes, deploy or rebase instead if a draft is actually present with different args","Check type, path, and trigger_kind spelling against the item that was edited"],"exampleFix":"// before\ndiscard_draft({ type: 'script', path: 'u/admin/cleanup' }) // no draft exists\n// after\n// rebase_draft can recover changes only if a draft exists; if none:\n// proceed without action — the item has no pending chat edits","handlingStrategy":"validation","validationCode":"const draft = await getGlobalDraft(workspace, args.type, args.path, args.trigger_kind)\nif (!draft) {\n  return // nothing to discard\n}","typeGuard":"function draftExists(d: unknown): boolean {\n  return d !== null && d !== undefined\n}","tryCatchPattern":"try {\n  await discardDraft(args)\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('No draft found')) {\n    // no pending chat edits — proceed without action\n  }\n}","preventionTips":["Check draft existence before discard; make discards idempotent","Don't discard after a successful deploy — deploying clears the draft","Verify exact type/path/trigger_kind match what was edited"],"tags":["ai-agent","not-found","draft-management"],"backgroundTag":"draft-not-found","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"}