{"record":{"id":"997ead4481376fde","repo":"windmill-labs/windmill","slug":"no-unsaved-draft-at-path-to-discard","errorCode":null,"errorMessage":"No unsaved draft at '${path}' to discard.","messagePattern":"No unsaved draft at '(.+?)' to discard\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/assets/AssetGraph/pipelineAiHelpers.ts","lineNumber":343,"sourceCode":"\t\t\t\tlocalId: existing?.localId ?? deps.newDraftLocalId(),\n\t\t\t\tscript: { ...baseScript, content },\n\t\t\t\toutputAssets,\n\t\t\t\tinputAssets: inferred.reads\n\t\t\t})\n\t\t\tdeps.setDrafts(next)\n\t\t\tdeps.onShowDrafts?.()\n\t\t\tdeps.onProposeNode?.(path)\n\t\t\t// Deployable lineage only (see proposeNode): body writes + materialize target.\n\t\t\treturn {\n\t\t\t\tdetectedReads: inferred.reads.map(assetUri),\n\t\t\t\tdetectedWrites: dedupeAssets([...inferred.writes, ...materializeWrites(content)]).map(\n\t\t\t\t\tassetUri\n\t\t\t\t)\n\t\t\t}\n\t\t},\n\t\tremoveProposedNode: async (path) => {\n\t\t\tif (!deps.getDrafts().has(path)) {\n\t\t\t\tthrow new Error(`No unsaved draft at '${path}' to discard.`)\n\t\t\t}\n\t\t\tconst next = new Map(deps.getDrafts())\n\t\t\tnext.delete(path)\n\t\t\tdeps.setDrafts(next)\n\t\t\tdeps.onForgetPath?.(path)\n\t\t},\n\t\ttestNode: async (path, args) => {\n\t\t\tconst workspace = deps.getWorkspace()\n\t\t\tif (!workspace) return undefined\n\t\t\tconst draft = deps.getDrafts().get(path)\n\t\t\ttry {\n\t\t\t\tlet jobId: string\n\t\t\t\tif (draft) {\n\t\t\t\t\t// Un-deployed/edited body: preview-run the draft content so it can be\n\t\t\t\t\t// tested before deploying.\n\t\t\t\t\tjobId = await JobService.runScriptPreview({\n\t\t\t\t\t\tworkspace,\n\t\t\t\t\t\trequestBody: {","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/assets/AssetGraph/pipelineAiHelpers.ts#L325-L361","documentation":"Thrown by removeProposedNode in createPipelineAiHelpers when asked to discard a draft at `path` that is not present in the current drafts map. The pipeline AI proposes asset-pipeline nodes as unsaved drafts; this fires when the tool targets a path that was never proposed or was already discarded — it is a sentinel guard, not an unexpected failure.","triggerScenarios":"Thrown at frontend/src/lib/components/assets/AssetGraph/pipelineAiHelpers.ts:343 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Check `path` against the currently proposed/open drafts before calling removeProposedNode","Refresh the drafts list to confirm the draft still exists and use its exact path","If the draft was already discarded, treat the operation as already done instead of retrying"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"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"}