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

  1. 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)
  2. Recover the script source from git history of the originally pulled file, restore it, and re-push
  3. If the corruption is on the server only, recreate the script body in the Windmill UI and save a new flow version
  4. 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

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


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/a20b5a255af07b0a. Report an issue: GitHub.