windmill-labs/windmill · critical
Refusing to extract corrupted inline script for module '${id
Error message
Refusing to extract corrupted inline script for module '${id}': rawscript.content is the literal string `${content.split("\n")[0]}` instead of script source. The backend's flow_version.value is corrupt — re-push from a known-good local copy to repair it. What it means
extractRawscriptInline extracts inline script bodies from flow YAML back into separate files. If a rawscript module's `content` is itself the literal string `!inline <path>` (instead of actual script source), the backend's stored flow_version.value was poisoned by a prior push that uploaded the unresolved directive as the script body. Because writing that string back would silently corrupt the file (and could clobber the real local source), the extractor refuses — but only when `failOnInlineDirective` is enabled, since legitimate local YAML flows parsed in memory do carry `!inline` content.
Source
Thrown at cli/windmill-utils-internal/src/inline-scripts/extractor.ts:38
rawscript: RawScript,
mapping: Record<string, string>,
separator: string,
assigner: PathAssigner,
failOnInlineDirective: boolean
): InlineScript[] {
const [basePath, ext] = assigner.assignPath(summary ?? id, rawscript.language);
const mappedPath = mapping[id];
const path = mappedPath ?? basePath + ext;
const language = rawscript.language;
const content = rawscript.content;
// Opt-in defensive guard: when extracting from backend-shaped data (i.e.
// sync pull), a rawscript whose content is itself an `!inline ...` directive
// means the backend was poisoned by a prior push that sent the unresolved
// directive as the script body (GIT-871 / #9140). Refuse to write it back
// to disk. Off by default because callers that operate on YAML-parsed local
// flows (flow_metadata, dev) legitimately see `!inline foo.ts` as content.
if (failOnInlineDirective && typeof content === "string" && content.startsWith("!inline ")) {
throw new Error(
`Refusing to extract corrupted inline script for module '${id}': ` +
`rawscript.content is the literal string \`${content.split("\n")[0]}\` ` +
`instead of script source. The backend's flow_version.value is corrupt — ` +
`re-push from a known-good local copy to repair it.`
);
}
const r = [{ path: path, content: content, language, is_lock: false}];
rawscript.content = "!inline " + path.replaceAll(separator, "/");
const lock = rawscript.lock;
if (lock && lock != "") {
// Derive lock path base from the mapped content path when available,
// so lock files are named consistently with their content files.
const lockBasePath = mappedPath
? lockBasePathForContent(mappedPath, language)
: basePath;
const lockPath = lockBasePath + "lock";
rawscript.lock = "!inline " + lockPath.replaceAll(separator, "/");
r.push({ path: lockPath, content: lock, language, is_lock: true});View on GitHub (pinned to e474e8803c)
Solutions
- Repair the backend by re-pushing the flow from a known-good local copy where rawscript.content holds the real script source (run `wmill sync push` from the intact checkout or git history)
- Recover the script source from git history of the originally pulled file, restore it, and re-push
- If the corruption is on the server only, recreate the script body in the Windmill UI and save a new flow version
- Only if you are sure the `!inline` content is legitimate local state, disable failOnInlineDirective — do not use this to mask real corruption
Example fix
// before (poisoned backend state) // flow_version.value rawscript content = "!inline user_script.ts" // after (repair from local source) // local flow YAML rawscript: // content: !inline user_script.ts # with user_script.ts holding real source // $ wmill sync push # re-resolves and repairs the backend
Defensive patterns
Strategy: validation
Validate before calling
function isPoisonedInline(content: unknown): boolean {
return typeof content === 'string' && content.startsWith('!inline ');
}
// before pulling/overwriting local files:
if (isPoisonedInline(module.value?.content)) {
console.error(`Backend flow ${path} is corrupted; re-push from a good copy`);
} Type guard
function isRealScriptSource(v: unknown): v is string {
return typeof v === 'string' && v.length > 0 && !v.startsWith('!inline ');
} Try / catch
try {
await extractInlineScripts(flowYaml, { failOnInlineDirective: true });
} catch (e) {
if ((e as Error).message.includes('corrupted inline script')) {
console.error('Backend flow is corrupt — restore local source from git and re-push');
} else throw e;
} Prevention
- Never hand-edit `!inline` content blocks in pulled YAML and push them back unresolved
- Keep pulled flows in git so a poisoned backend can be repaired from history
- After suspicious pushes, pull and diff — a diff where script files become one-line `!inline` strings is the corruption signature
- Run push from a checkout where the referenced inline files exist locally
When it happens
Trigger: extractInlineScripts (pull/dev sync against a remote flow) with failOnInlineDirective=true, encountering a rawscript module whose stored content starts with `!inline ` — a prior push round-tripped the directive to the server instead of resolving it to source.
Common situations: A previous pull produced YAML files whose inline scripts were then pushed back unmodified (directive leaked to backend); syncing a flow corrupted by GIT-871 / issue #9140; re-pulling an already-poisoned flow version repeatedly.
Related errors
- Cannot push flow ${remotePath}: missing inline script file(s
- Module value is undefined for module ${module.id}
- Unresolved inline script references: ${unresolvedRefs.join('
- not a flow
- Cannot push flow ${remotePath}: step(s) reference non-worksp
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/a20b5a255af07b0a.
Report an issue: GitHub.