1Panel-dev/1Panel · error · ValueError
outside-module-root
Error message
outside-module-root
What it means
Raised after resolve(strict=True) when the fully resolved path is not under the modules root (modules_root not in full_path.parents). This catches escapes that survived the lexical checks: symlinks in intermediate directories (parent dir is a link even if the final file is not) or a modules_root that is itself a symlink making the resolved parents diverge.
Source
Thrown at scripts/openresty-modules/diagnose-install.sh:223
print("module\tbuild_status\ttarget_key\tartifact\texpected\tactual\tresult")
for module in modules:
for build in module.get("builds") or []:
target_key = (build.get("target") or {}).get("key", "")
for artifact in build.get("artifacts") or []:
relative = artifact.get("path", "")
expected = artifact.get("checksum", "")
result = "OK"
actual = ""
try:
pure = pathlib.PurePosixPath(relative)
if not relative or pure.is_absolute() or ".." in pure.parts or "\\" in relative:
raise ValueError("unsafe-path")
candidate = modules_root / pathlib.Path(*pure.parts)
if candidate.is_symlink():
raise ValueError("symlink-not-allowed")
full_path = candidate.resolve(strict=True)
if modules_root not in full_path.parents:
raise ValueError("outside-module-root")
if not full_path.is_file():
raise ValueError("not-regular-file")
digest = hashlib.sha256()
with full_path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
actual = digest.hexdigest()
if actual.lower() != expected.lower():
raise ValueError("checksum-mismatch")
except Exception as error:
result = str(error)
failed = True
print("\t".join([
module.get("name", ""), build.get("status", ""), target_key,
relative, expected, actual, result,
]))
sys.exit(1 if failed else 0)
PYView on GitHub (pinned to 5ac7c80881)
Solutions
- find <modules_root> -type l and replace any directory symlinks with real directories (or bind mounts)
- Ensure modules_root passed to the script is the canonical resolved path (realpath) so the parents check compares against the same tree the artifacts resolve into
- Re-run diagnose-install.sh; the row must show result=OK
Example fix
# before modules/resty -> /srv/resty (symlinked dir) # after mkdir modules/resty && cp -L /srv/resty/* modules/resty/
Defensive patterns
Strategy: validation
Validate before calling
python3 -c "import os; r=os.path.realpath('/opt/1panel/openresty/modules'); print(r)" # pass this resolved path as modules_root Prevention
- Use realpath for the modules root on both producer and verifier sides
- Avoid symlinked data volumes for the modules directory; prefer bind mounts
When it happens
Trigger: An intermediate directory under modules_root is a symlink pointing elsewhere (modules/resty -> /srv/resty), so the artifact resolves outside; or modules_root was derived from a different symlinked path than the one used for comparison, so parents never match.
Common situations: The modules directory is on a bind mount or symlinked data volume; a Docker volume mount maps the modules dir to another host path; admin symlinked a whole subdirectory for space reasons.
Related errors
AI-assisted analysis of 1Panel-dev/1Panel@5ac7c80881 (2026-08-15).
Data as JSON: /api/errors/3a8fce0bd812a0c2.
Report an issue: GitHub.