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 trueView on GitHub (pinned to e474e8803c)
Solutions
- Configure the gem source credentials (host, user, password) in Windmill instance settings so the proxy injects them
- Remove the user:password portion from the source URL in the Gemfile and reference the plain host
- Ask an administrator to add the credentials if you lack instance-settings access
- 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
- Never embed user:password in Gemfile source URLs
- Register private gem server credentials in instance settings
- Use env-var or credential-store patterns locally too
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
- bundle failed:\n${attempt.output}
- no policy could be derived for runnable(s) ${malformed.join(
- result.substring(__ERR_PREFIX.length)
- `require 'windmill/inline'` is not detected - please add `re
- raw app path ${JSON.stringify(relPath)} escapes the app fold
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/ea791fbd642af19f.
Report an issue: GitHub.