windmill-labs/windmill · error · Error

no policy could be derived for runnable(s) ${malformed.join(

Error message

no policy could be derived for runnable(s) ${malformed.join(', ')}: each must be an object with a `type` of "inline" (with `inlineScript.content`) or "path" (with `path` and a `runType` of ${RUN_TYPES.join(', ')})

What it means

When post-processing the Gemfile.lock to map locked gems to proxied installs, Windmill parses each LOCKED line expecting '<package> <version>'. If a lockfile line does not split into at least package and version tokens, this error names the unparsable line.

Source

Thrown at backend/windmill-api/src/apps_raw_bundler.ts:154

	// The prefixes `execute_component` resolves a run against; anything else is a
	// grant no run can match.
	const RUN_TYPES = ['script', 'flow', 'hubscript']
	const malformed = Object.entries(runnables ?? {})
		.filter(([, r]) => r != null)
		.filter(([, r]) => {
			if (typeof r !== 'object') return true
			const run = r as Record<string, any>
			if (run.type === 'inline' || run.type === 'runnableByName') {
				return !nonEmpty(run.inlineScript?.content)
			}
			if (run.type === 'path' || run.type === 'runnableByPath') {
				return !RUN_TYPES.includes(run.runType) || !nonEmpty(run.path)
			}
			return true
		})
		.map(([id]) => id)
	if (malformed.length > 0) {
		throw new Error(
			`no policy could be derived for runnable(s) ${malformed.join(', ')}: each must be an ` +
				`object with a \`type\` of "inline" (with \`inlineScript.content\`) or "path" (with ` +
				`\`path\` and a \`runType\` of ${RUN_TYPES.join(', ')})`
		)
	}

	// The policy's `triggerables_v2` is the allowlist the server matches every run
	// against, keyed by a hash of each inline runnable's code. Derived by the
	// frontend's own code, bundled into this script by cli/generate-app-policy.ts,
	// so the keys are the ones the app editor writes: anything else leaves the
	// app's runnables "forbidden by policy". Prepended above as a plain `var`, so
	// it is in this module's scope (a module's top-level `var` is not a global).
	const { triggerables_v2 } = await __wmillAppPolicy.updateRawAppPolicy(
		runnables ?? {},
		undefined
	)

	// Gzipped so a large app's bundle stays well inside MAX_RESULT_SIZE_MB, which

View on GitHub (pinned to e474e8803c)

Solutions

  1. Delete Gemfile.lock locally and regenerate it with bundler lock / bundle install, then redeploy the script
  2. Inspect the named line in the lockfile and fix or remove the malformed entry
  3. Regenerate the lockfile with a bundler version compatible with the worker's Ruby
  4. If a specific gem produces the line, pin it to a version with a standard lockfile representation

Example fix

# before (hand-edited lockfile line)
  mygem ()
# after (regenerate)
  mygem (1.2.3)
Defensive patterns

Strategy: validation

Validate before calling

function validateLockfile(lock) {
  const lines = lock.split('\n').filter(l => /^\s{4}\S/.test(l));
  for (const l of lines) {
    const buf = l.replace(/[()]/g, '').trim();
    const parts = buf.split(/\s+/);
    if (parts.length < 2 || !parts[1]) throw new Error(`malformed lock line: "${l.trim()}"`);
  }
}

Try / catch

try {
  await deployRubyJob({ gemfile, lockfile });
} catch (e) {
  if (/Cannot determine version and package name/.test(e.message)) {
    throw new Error(`Regenerate Gemfile.lock; offending line: ${e.message.split('for: ')[1]}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: install() parses Gemfile.lock lines and a line in the spec/lock section has an unexpected format (empty tokens, unusual gem name with spaces after stripping parentheses, malformed lockfile).

Common situations: A corrupted or hand-edited Gemfile.lock; a lockfile generated by an incompatible bundler version with new line formats; a gem whose name/version contains characters the parser doesn't expect.

Understand the failure class

Related errors


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