{"record":{"id":"4915d09a6f154edd","repo":"windmill-labs/windmill","slug":"internal-error-path-is-not-under-repository-di","errorCode":null,"errorMessage":"Internal Error: {path} is not under {repository_dir}","messagePattern":"Internal Error: (.+?) is not under (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-worker/src/java_executor.rs","lineNumber":566,"sourceCode":"                w.found = true;\n                return Ok(());\n            }\n        }\n        for (name, entry) in subdirs {\n            below.push(name);\n            find_and_copy(&entry.path(), below, wanted).await?;\n            below.pop();\n        }\n        Ok(())\n    }\n\n    let mut wanted = deps\n        .iter()\n        .map(|RequiredDependency { path, display_name, .. }| {\n            let suffix = path\n                .strip_prefix(repository_dir)\n                .filter(|suffix| suffix.starts_with('/'))\n                .ok_or_else(|| anyhow!(\"Internal Error: {path} is not under {repository_dir}\"))?;\n            Ok(Wanted {\n                coordinate: suffix\n                    .split('/')\n                    .filter(|component| !component.is_empty())\n                    .map(str::to_owned)\n                    .collect(),\n                destination: path.clone(),\n                display_name: display_name.clone(),\n                found: false,\n            })\n        })\n        .collect::<anyhow::Result<Vec<_>>>()?;\n    // longest coordinate first: a group id ending in another one's coordinates (com.org.foo:bar\n    // over org.foo:bar) would otherwise be free to claim the shorter one's directory\n    wanted.sort_by_key(|w| std::cmp::Reverse(w.coordinate.len()));\n\n    find_and_copy(&PathBuf::from(fetch_dir), &mut vec![], &mut wanted).await?;\n","sourceCodeStart":548,"sourceCodeEnd":584,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-worker/src/java_executor.rs#L548-L584","documentation":"The Java executor's Maven dependency cache (`move_to_repository`) receives a `RequiredDependency.path` that is expected to live inside `repository_dir` (the local Maven repository). `strip_prefix` fails when the path isn't actually a subpath of the repository dir, meaning an internal invariant is violated — this is always a bug in how the dependency path was computed or cached, not a user-input error.","triggerScenarios":"A cached dependency's stored path was written with a different repository_dir (changed `MAVEN_REPO` config, moved cache, different worker/container mount), or path construction produced an absolute path outside the repo (e.g. registry URL trickery — see tests like `artifacts_are_found_whatever_the_registry_url_path_is`).","commonSituations":"Worker cache persisted across a Windmill version where the repository layout changed; Docker volume mounted at a different path than when artifacts were downloaded; a malicious/misconfigured Maven registry returning a coordinate whose resolved path escapes the repository (path-traversal guard firing).","solutions":["Clear the Maven dependency cache/repository directory so paths are regenerated under the current `repository_dir`.","Ensure the repository dir mount point is identical between the run that cached artifacts and this run (same Docker volume path, same `MAVEN_REPO` setting).","If this fires on registry-supplied coordinates, audit the registry URL — the path likely contains `..` or an absolute component escaping the repo; fix the registry configuration.","If reproducible on a fresh cache, file a bug — the path construction in the java executor is violating its own invariant."],"exampleFix":"// before: cache written with old repo dir\n// /old/repo/org/example/foo/1.0/foo-1.0.jar vs repo=/new/repo\n// after: wipe and repopulate the cache under the current repo\nrm -rf /new/repo && wmill worker start # re-downloads dependencies","handlingStrategy":"validation","validationCode":"// before installing, sanity-check every dep path sits under the repo\nfor dep in deps {\n    if !dep.path.starts_with(repository_dir) {\n        return Err(format!(\"{} is outside repository {}\", dep.path, repository_dir));\n    }\n}","typeGuard":"fn under_repo(path: &Path, repo: &Path) -> bool {\n    path.strip_prefix(repo).map(|s| s.starts_with(\"/\")).unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Mount the Maven repository at a stable, identical path across all workers and restarts","Wipe the cache after upgrading Windmill or changing the repository dir","Only use trusted Maven registries; path-escaping coordinates are treated as a bug/guard"],"tags":["java","maven","path","internal-error"],"backgroundTag":"path-outside-expected-root","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"}