windmill-labs/windmill · error · Error

bundle produced no javascript:\n${buildOutput}

Error message

bundle produced no javascript:\n${buildOutput}

What it means

Windmill rewrites Gemfile `source` lines to route through its internal gem proxy, and inline credentials (user:password embedded in the source URL) cannot be carried through that rewriting. The error redacts the credentials and names the offending source line.

Source

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

		} else if (!/Unknown command|Usage:\s+wmill app\b/.test(plain)) {
			throw new Error(`bundle failed:\n${attempt.output}`)
		}
	}
	if (buildOutput === undefined) {
		buildOutput = run([...cli_command, dir, '--out', outDir], 'bundle')
	}

	const read = async (name: string) => {
		try {
			return await fs.readFile(path.join(outDir, name), 'utf8')
		} catch {
			return ''
		}
	}
	const js = await read('bundle.js')
	const css = await read('bundle.css')
	if (js === '') {
		throw new Error('bundle produced no javascript:\n' + buildOutput)
	}

	// A runnable the derivation can't fully classify still yields a key, just an
	// unusable one (`r:undefined/undefined`, or the hash of an absent script), and
	// the deploy would then succeed with grants no run can ever match. The tool
	// schema describes `runnables` only as an object and the on-disk format has no
	// discriminator at all (`wmill app push` adds it), so these shapes are all
	// reachable: check them before deriving and name the ones at fault. An
	// explicitly empty entry is a runnable nobody configured yet, and needs no
	// grant.
	const nonEmpty = (v: unknown) => typeof v === 'string' && v.length > 0
	// 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

View on GitHub (pinned to e474e8803c)

Solutions

  1. Configure the gem source credentials (host, user, password) in Windmill instance settings so the proxy injects them
  2. Remove the user:password portion from the source URL in the Gemfile and reference the plain host
  3. Ask an administrator to add the credentials if you lack instance-settings access
  4. Contact Windmill support/feature requests if inline credentials are essential to your workflow

Example fix

// before
source 'https://user:secret@gems.example.com'
// after
source 'https://gems.example.com'  # credentials configured in instance settings
Defensive patterns

Strategy: validation

Validate before calling

function validateGemfile(gemfile) {
  const m = gemfile.match(/^\s*source\s*['"]\S+:\/\/[^/'"]+:[^/'"]+@/m);
  if (m) throw new Error('Gemfile contains inline source credentials — configure them in instance settings instead');
}

Try / catch

try {
  await runRubyJob(gemfile);
} catch (e) {
  if (/inline source credentials are not supported/.test(e.message)) {
    throw new Error('Strip user:password from the source URL and add credentials in instance settings');
  }
  throw e;
}

Prevention

When it happens

Trigger: A Gemfile inside a ruby job contains a source URL of the form scheme://user:password@host — matched by UNSUPPORTED_SOURCE_RE during resolve.

Common situations: Private Gemfury/Artifactory/rubygems-hosting URLs pasted with embedded basic-auth credentials; scripts migrated from CI setups where credentialed URLs were the norm.

Related errors


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