{"record":{"id":"db681d9af64b7c6a","repo":"windmill-labs/windmill","slug":"file-path-escapes-the-build-directory-rel","errorCode":null,"errorMessage":"file path escapes the build directory: ${rel}","messagePattern":"file path escapes the build directory: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"backend/windmill-api/src/apps_raw_bundler.ts","lineNumber":41,"sourceCode":"\t// Set unless the server was told to build with a specific command.\n\tprefer_installed_cli: boolean | undefined,\n\t// The app's `value.runnables`, whose policy is derived here for the same\n\t// reason the bundle is built here: it has to match what the editor writes.\n\trunnables: Record<string, unknown> | undefined\n): Promise<{ js_gz: string; css_gz: string; triggerables_v2: Record<string, unknown> }> {\n\tconst fs = await import('node:fs/promises')\n\tconst path = await import('node:path')\n\n\tconst dir = path.join(process.cwd(), 'wm_raw_app')\n\tawait fs.rm(dir, { recursive: true, force: true })\n\n\t// Where a key lands, `path.join` normalising `./` and `..` away. Everything\n\t// that reasons about a file goes through this, so nothing disagrees with what\n\t// was actually written.\n\tconst target = (rel: string) => {\n\t\tconst abs = path.join(dir, rel.replace(/^\\/+/, ''))\n\t\tif (!abs.startsWith(dir + path.sep)) {\n\t\t\tthrow new Error(`file path escapes the build directory: ${rel}`)\n\t\t}\n\t\treturn abs\n\t}\n\tconst write = async (rel: string, content: string) => {\n\t\tconst abs = target(rel)\n\t\tawait fs.mkdir(path.dirname(abs), { recursive: true })\n\t\tawait fs.writeFile(abs, content)\n\t}\n\tfor (const [p, content] of Object.entries(files ?? {})) {\n\t\tawait write(p, content)\n\t}\n\t// `ui/` next to the app is where `wmill app bundle` looks for the shared UI.\n\tfor (const [p, content] of Object.entries(shared_ui ?? {})) {\n\t\tawait write('ui/' + p.replace(/^\\/+/, ''), content)\n\t}\n\n\tconst manifest = path.join(dir, 'package.json')\n\tconst hasPackageJson = Object.keys(files ?? {}).some((p) => target(p) === manifest)","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-api/src/apps_raw_bundler.ts#L23-L59","documentation":"When a base-env-derived reduced install path fails, Windmill falls back to building the environment and reports this generic error if that fallback did not succeed. It signals the whole python environment preparation failed; the actionable detail is expected to be in the preceding logs.","triggerScenarios":"handle_python_reqs attempts an env build (full or reduced limit) and the build/install routine returns Err; also thrown when a retry of the reduced install path fails after the first attempt already failed.","commonSituations":"pip resolution conflicts among the declared requirements; unsupported python version for a pinned package; network failure reaching PyPI; worker disk full; OOM during wheel build for source-only packages.","solutions":["Read the full worker/job logs above this message — pip's real error (resolver conflict, build failure, network) is printed there","Simplify/relax requirement version pins so the resolver can find a compatible set","Retry after checking worker disk space and network access to the package index","Use a prebuilt python-based worker image that already contains your dependencies to skip install at run time","If OOM occurred, increase worker memory or install fewer deps per job"],"exampleFix":"// before (conflicting pins)\nrequirements = \"pandas==2.0.0 numpy==2.1.0\"\n// after (compatible set)\nrequirements = \"pandas==2.2.2 numpy==2.0.1\"","handlingStrategy":"retry","validationCode":"// Pre-validate requirements resolve (dry run)\nconst { execSync } = require('child_process');\nfunction requirementsResolve(reqs, pyVer) {\n  try {\n    execSync(`uv pip compile -q --python-version ${pyVer} -`, { input: reqs.join('\\n') });\n    return true;\n  } catch (e) { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await deployScript({ requirements, pythonVersion });\n} catch (e) {\n  if (/Env installation did not succeed/.test(e.message)) {\n    // detail is in the logs — surface them before retrying\n    const logs = await getJobLogs(jobId);\n    throw new Error(`env build failed; pip said: ${logs.match(/(ERROR|error):.*/g)?.join('; ')}`);\n  }\n  throw e;\n}","preventionTips":["Test-compile requirements locally (pip compile / uv pip compile) before deploying","Avoid mutually conflicting pins; let a lockfile drive versions","Prebuild dependencies into a custom worker image","Check worker disk/network before large installs"],"tags":["python","pip","environment-build","dependency-resolution"],"backgroundTag":"python-env-install-failed","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}