{"record":{"id":"a64b8cb5f8fb2e55","repo":"windmill-labs/windmill","slug":"local-script-path-has-no-content-language","errorCode":null,"errorMessage":"local script ${path} has no content/language","messagePattern":"local script (.+?) has no content/language","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/assets/AssetGraph/cascadeRun.ts","lineNumber":86,"sourceCode":"\ttempScriptRefs?: Record<string, string>\n\t// Extra run args for a specific node (e.g. the uploaded S3Object bound to a\n\t// `data_upload` cascade root). Merged over `_wmill_skip_asset_dispatch`; all\n\t// other nodes run with empty inputs as before.\n\targsFor?: (path: string) => Record<string, any> | undefined\n\tonLaunched?: (path: string, jobId: string) => void\n}): (path: string) => Promise<string> {\n\treturn async function launch(path: string): Promise<string> {\n\t\tconst local = opts.resolveLocal?.(path)\n\t\t// Caller args (e.g. the run form for the cascade root) must NOT be able to\n\t\t// re-enable backend asset dispatch while the client orchestrates the closure\n\t\t// — that would double-run downstream / run deployed subscribers. Drop any\n\t\t// `_wmill_skip_asset_dispatch` a caller supplied, and always spread it LAST.\n\t\tconst { _wmill_skip_asset_dispatch: _reserved, ...extra } = opts.argsFor?.(path) ?? {}\n\t\tvoid _reserved\n\t\tlet jobId: string\n\t\tif (local) {\n\t\t\tif (!local.content || !local.language) {\n\t\t\t\tthrow new Error(`local script ${path} has no content/language`)\n\t\t\t}\n\t\t\tjobId = await JobService.runScriptPreview({\n\t\t\t\tworkspace: opts.workspace,\n\t\t\t\trequestBody: {\n\t\t\t\t\tcontent: local.content,\n\t\t\t\t\tlanguage: local.language,\n\t\t\t\t\tpath,\n\t\t\t\t\targs: { ...extra, _wmill_skip_asset_dispatch: true },\n\t\t\t\t\t...(local.tag ? { tag: local.tag } : {}),\n\t\t\t\t\t...(opts.tempScriptRefs ? { temp_script_refs: opts.tempScriptRefs } : {})\n\t\t\t\t}\n\t\t\t})\n\t\t} else {\n\t\t\tjobId = await JobService.runScriptByPath({\n\t\t\t\tworkspace: opts.workspace,\n\t\t\t\tpath,\n\t\t\t\trequestBody: { ...extra, _wmill_skip_asset_dispatch: true }\n\t\t\t})","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/assets/AssetGraph/cascadeRun.ts#L68-L104","documentation":"Before launching a local (workspace-resident) pipeline script during a cascade run, makeLaunch checks that the script has both `content` and `language`. It throws this error when a locally resolved script came back from the workspace without usable body or language, since runScriptPreview requires both fields.","triggerScenarios":"`resolveLocal(path)` returned a script object with empty/undefined `content` or `language` — e.g. the script exists but its deployment metadata was fetched instead of full content, the script is a draft/empty stub, or the resolver silently returned a skeleton object for a missing path.","commonSituations":"Script deployed from a flow without content; script fetched with `include_content` off or a partial get; asset graph pointing at a node whose underlying script was deleted or emptied; language not persisted for very old scripts or manually inserted DB rows.","solutions":["Open the script at that path in the Windmill hub and re-save/deploy it with real content and a language","Re-fetch the script with content included (ensure the resolver requests full script data, not metadata-only)","If the path is wrong, correct the asset/dependency reference to point at the deployed script","For generated drafts, populate `content` and `language` before dispatching the cascade run"],"exampleFix":"// before\nconst local = await resolveLocal(path) // { path } only — no content\nawait launch(path)\n// after\nconst local = await getScriptByPath({ workspace, path }) // full content + language\nif (!local?.content || !local?.language) throw new Error(`script ${path} missing on workspace`)\nawait launch(path)","handlingStrategy":"validation","validationCode":"const local = await resolveLocal(path)\nif (!local?.content?.trim() || !local?.language) {\n  throw new Error(`local script ${path} is empty or has no language; redeploy it before running the cascade`)\n}","typeGuard":"function isRunnableScript(s: { content?: string | null; language?: string | null } | null): s is { content: string; language: string } {\n  return !!s && typeof s.content === 'string' && s.content.trim().length > 0 && typeof s.language === 'string'\n}","tryCatchPattern":"try {\n  await launch(path)\n} catch (e) {\n  if (e instanceof Error && e.message.includes('has no content/language')) {\n    const fresh = await getScriptByPath({ workspace, path }) // re-fetch full content\n    if (!fresh?.content || !fresh?.language) throw new Error(`script ${path} missing on workspace; fix the asset reference`)\n    return launch(path, fresh)\n  }\n  throw e\n}","preventionTips":["Always fetch scripts with content included, never metadata-only responses","Verify referenced script paths exist and are fully deployed before cascade runs","Don't point asset graph nodes at empty drafts","Re-save scripts after imports/migrations that could strip content"],"tags":["windmill","scripts","validation","asset-graph"],"backgroundTag":"missing-script-content","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"}