{"record":{"id":"4d4eace2fcacfbd4","repo":"windmill-labs/windmill","slug":"pipeline-nodes-must-be-in-the-open-folder-use-f","errorCode":null,"errorMessage":"Pipeline nodes must be in the open folder — use 'f/${folder}/${node}' (got '${path}').","messagePattern":"Pipeline nodes must be in the open folder — use 'f/(.+?)/(.+?)' \\(got '(.+?)'\\)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/assets/AssetGraph/pipelineAiHelpers.ts","lineNumber":150,"sourceCode":"\t\treturn true\n\t})\n}\n\nexport function createPipelineAiHelpers(deps: PipelineAiHelperDeps): PipelineAIChatHelpers {\n\tconst folderName = () => normalizePipelineFolder(deps.getFolder())\n\n\t// A staged draft is always persisted into the OPEN folder's data_pipeline\n\t// bundle, so a path outside the folder would silently land an unrelated script\n\t// there. Both build and edit must stay scoped to the folder.\n\tfunction assertInFolder(path: string) {\n\t\tconst folder = folderName()\n\t\tif (folder && !path.startsWith(`f/${folder}/`)) {\n\t\t\t// Name the corrected path rather than only the required prefix: the model\n\t\t\t// otherwise re-sends variants of the same wrong path. A path that is the\n\t\t\t// folder itself has no node leaf to reuse.\n\t\t\tconst leaf = path.split('/').filter(Boolean).pop()\n\t\t\tconst node = !leaf || leaf === folder ? '<node_name>' : leaf\n\t\t\tthrow new Error(\n\t\t\t\t`Pipeline nodes must be in the open folder — use 'f/${folder}/${node}' (got '${path}').`\n\t\t\t)\n\t\t}\n\t}\n\n\t// A pipeline node IS its `// pipeline` annotation (it's what makes the deployed\n\t// script a pipeline member). Reject content that lacks it so a staged draft\n\t// isn't a non-member script the model can't see is broken until deploy.\n\tfunction assertPipelineAnnotation(content: string) {\n\t\tif (!parsePipelineAnnotations(content).inPipeline) {\n\t\t\tthrow new Error(\n\t\t\t\t`Pipeline node content must declare the pipeline annotation on its own comment line ` +\n\t\t\t\t\t`(\\`// pipeline\\`, or \\`-- pipeline\\` for SQL / \\`# pipeline\\` for Python).`\n\t\t\t)\n\t\t}\n\t}\n\n\tfunction buildContext(): PipelineContext {","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/assets/AssetGraph/pipelineAiHelpers.ts#L132-L168","documentation":"Pipeline AI helpers validate that any node path the model or caller touches belongs to the currently open pipeline folder. assertInFolder throws this error when the given path does not start with `f/${folder}/`, i.e. the path references a node outside the open folder (or the folder itself, with no node leaf).","triggerScenarios":"Calling a pipeline AI helper (read/update/create node) with a path like `f/other_folder/node`, a root-level `u/user/script`, or the bare folder path `f/my_folder` while folder is open; the model hallucinating a path from an earlier context.","commonSituations":"LLM reusing node paths from a previously opened pipeline; constructing paths from memory instead of the provided folder listing; passing the folder itself instead of a node inside it; typos in the folder segment of the path.","solutions":["Rewrite the call using the required form `f/${folder}/<node_name>` as the error message suggests","List the folder's nodes first and pick a node that exists in the currently open folder","If you meant a different folder, open/switch to that folder before calling the helper","If you passed the folder itself, append the node leaf: `f/${folder}/my_node`"],"exampleFix":"// before\nawait updateNode('u/admin/pipeline_step', content)\nawait updateNode('f/my_folder', content)\n// after\nawait updateNode('f/my_folder/pipeline_step', content)","handlingStrategy":"validation","validationCode":"function assertPathInFolder(path: string, folder: string) {\n  if (folder && !path.startsWith(`f/${folder}/`)) {\n    throw new Error(`path ${path} is outside open folder f/${folder}/`)\n  }\n}\nassertPathInFolder(nodePath, openFolder) // call before any pipeline helper","typeGuard":"function isInFolder(path: string, folder: string): boolean {\n  return !!folder && path.startsWith(`f/${folder}/`)\n}","tryCatchPattern":"try {\n  await updateNode(path, content)\n} catch (e) {\n  if (e instanceof Error && e.message.includes('must be in the open folder')) {\n    const m = e.message.match(/use '([^']+)'/)\n    if (m) return updateNode(m[1], content) // retry with the corrected path the error suggests\n  }\n  throw e\n}","preventionTips":["Always derive node paths from the open folder listing, never from memory or earlier contexts","Copy paths verbatim from folder node lists instead of reconstructing them","Remember the folder alone is not a valid node path — always append a node leaf","For LLM tool calls, include the open folder name prominently in the prompt/context"],"tags":["path-validation","ai-tools","pipeline","windmill"],"backgroundTag":"path-outside-allowed-folder","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"}