{"record":{"id":"bd927e86cc73a97a","repo":"windmill-labs/windmill","slug":"a-draft-already-exists-at-path-use-edit-pipe","errorCode":null,"errorMessage":"A draft already exists at '${path}'. Use edit_pipeline_node to change it instead.","messagePattern":"A draft already exists at '(.+?)'\\. Use edit_pipeline_node to change it instead\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/assets/AssetGraph/pipelineAiHelpers.ts","lineNumber":240,"sourceCode":"\t\t\tif (!workspace) return undefined\n\t\t\ttry {\n\t\t\t\tconst deployed = await ScriptService.getScriptByPath({ workspace, path })\n\t\t\t\treturn { language: deployed.language, content: deployed.content }\n\t\t\t} catch {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t},\n\t\tproposeNode: async ({ path, language, content, outputKind }) => {\n\t\t\tdeps.ensureEditable?.()\n\t\t\t// build_pipeline_node creates a NEW node in the OPEN folder. Reject a path\n\t\t\t// outside the folder (it would silently stage into this folder's bundle)\n\t\t\t// and a path that collides with an existing node (the model should use\n\t\t\t// edit_pipeline_node instead of shadowing a deployed node as a draft).\n\t\t\tassertInFolder(path)\n\t\t\tassertPipelineAnnotation(content)\n\t\t\tconst drafts = deps.getDrafts()\n\t\t\tif (drafts.has(path)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`A draft already exists at '${path}'. Use edit_pipeline_node to change it instead.`\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (deps.getResolvedGraph().runnables.some((r) => r.path === path)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`A pipeline node already exists at '${path}'. Use edit_pipeline_node to change it instead.`\n\t\t\t\t)\n\t\t\t}\n\t\t\t// Authoritative new-node check: the resolved graph may not have hydrated yet\n\t\t\t// (the session preview can race open_preview), and it only lists pipeline\n\t\t\t// runnables — so probe the backend. ANY deployed script at this path means\n\t\t\t// \"build new\" would shadow it on deploy; the model should edit instead.\n\t\t\tconst workspace = deps.getWorkspace()\n\t\t\tif (workspace) {\n\t\t\t\tlet deployedExists = false\n\t\t\t\ttry {\n\t\t\t\t\tawait ScriptService.getScriptByPath({ workspace, path })\n\t\t\t\t\tdeployedExists = true","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/assets/AssetGraph/pipelineAiHelpers.ts#L222-L258","documentation":"pipelineAiHelpers proposeNode (build_pipeline_node tool) rejects a target path that already has a local draft in the open folder; the model asked to CREATE a node where a draft exists, which would silently overwrite staged work — the tool directs it to edit_pipeline_node instead.","triggerScenarios":"Thrown at frontend/src/lib/components/assets/AssetGraph/pipelineAiHelpers.ts:240 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Use edit_pipeline_node on that path to modify the existing draft.","Pick a different path if a genuinely new node is intended.","Discard the existing draft first if it should be replaced by a fresh build."],"exampleFix":null,"handlingStrategy":"validation","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"}