windmill-labs/windmill · error
Pipeline node content must declare the pipeline annotation o
Error message
Pipeline node content must declare the pipeline annotation on its own comment line (`// pipeline`, or `-- pipeline` for SQL / `# pipeline` for Python).
What it means
A Windmill pipeline node is defined by the `// pipeline` annotation comment in its script content (with per-language variants `-- pipeline` for SQL and `# pipeline` for Python). assertPipelineAnnotation parses the content and throws this error when the annotation is absent, so a staged draft can't silently become a non-member script.
Source
Thrown at frontend/src/lib/components/assets/AssetGraph/pipelineAiHelpers.ts:160
function assertInFolder(path: string) {
const folder = folderName()
if (folder && !path.startsWith(`f/${folder}/`)) {
// Name the corrected path rather than only the required prefix: the model
// otherwise re-sends variants of the same wrong path. A path that is the
// folder itself has no node leaf to reuse.
const leaf = path.split('/').filter(Boolean).pop()
const node = !leaf || leaf === folder ? '<node_name>' : leaf
throw new Error(
`Pipeline nodes must be in the open folder — use 'f/${folder}/${node}' (got '${path}').`
)
}
}
// A pipeline node IS its `// pipeline` annotation (it's what makes the deployed
// script a pipeline member). Reject content that lacks it so a staged draft
// isn't a non-member script the model can't see is broken until deploy.
function assertPipelineAnnotation(content: string) {
if (!parsePipelineAnnotations(content).inPipeline) {
throw new Error(
`Pipeline node content must declare the pipeline annotation on its own comment line ` +
`(\`// pipeline\`, or \`-- pipeline\` for SQL / \`# pipeline\` for Python).`
)
}
}
function buildContext(): PipelineContext {
const graph = deps.getResolvedGraph()
const drafts = deps.getDrafts()
const nodes: PipelineNodeSummary[] = graph.runnables
.filter((r) => r.usage_kind === 'script')
.map((r) => {
const draft = drafts.get(r.path)
const writes = graph.edges
.filter(
(e) =>
e.runnable_kind === 'script' &&View on GitHub (pinned to e474e8803c)
Solutions
- Add the annotation as its own first comment line matching the language: `// pipeline` (JS/TS), `-- pipeline` (SQL), `# pipeline` (Python)
- Ensure the annotation is a standalone line — not trailing code or inside a block comment/string
- Verify the comment token matches the script's language attribute
- Re-run the write/stage call after adding the annotation
Example fix
// before (JS/TS node)
export async function main() { ... }
// after
// pipeline
export async function main() { ... } Defensive patterns
Strategy: validation
Validate before calling
const ANNOTATION = /(^|\n)\s*(\/\/|--|#) pipeline\s*(\n|$)/
function hasPipelineAnnotation(content: string): boolean {
return ANNOTATION.test(content)
}
if (!hasPipelineAnnotation(draftContent)) {
draftContent = '// pipeline\n' + draftContent // or '# pipeline' for Python, '-- pipeline' for SQL
} Type guard
function declaresPipeline(content: string): boolean {
return parsePipelineAnnotations(content).inPipeline
} Try / catch
try {
await stageNode(path, content)
} catch (e) {
if (e instanceof Error && e.message.includes('pipeline annotation')) {
return stageNode(path, prependAnnotation(content, language))
}
throw e
} Prevention
- Always emit the annotation as the first line of generated pipeline content
- Match the comment token to the script language: // for JS/TS, -- for SQL, # for Python
- Keep it on its own line — no trailing code on the same line, no block comments
- Disable formatters/linters that strip leading comments from generated drafts
When it happens
Trigger: Writing or staging pipeline node content via the AI helpers where the content string lacks a standalone comment line `// pipeline` (or the SQL/Python equivalents) — e.g. the model generated a plain script body, the annotation was stripped by a formatter, or it was embedded inside another statement/string rather than on its own comment line.
Common situations: Model-generated content omitting the header comment; a code formatter removing or reflowing the comment; writing `#pipeline` (no space), inline trailing annotation like `foo() // pipeline` on a code line, or using the wrong comment token for the script language; content written with a block comment instead of a line comment.
Related errors
- local script ${path} has no content/language
- Pipeline nodes must be in the open folder — use 'f/${folder}
- draft ${path} has no content/language
- Field properties should be an object
- `oneOf` needs to be an array
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/ad4a1744fc1501c3.
Report an issue: GitHub.