infiniflow/ragflow · error · RuntimeError
Artifact symlinks are not allowed: {path.name}
Error message
Artifact symlinks are not allowed: {path.name} What it means
Raised by LocalProvider._collect_artifacts() while scanning the instance's artifacts directory when an entry is a symlink. Symlinks are rejected outright as a sandbox-escape / size-limit evasion vector: a symlink could point outside the instance directory (reading arbitrary host files) or be used to bypass per-file size checks. The whole run fails with RuntimeError and artifacts are not returned.
Source
Thrown at agent/sandbox/providers/local.py:320
@staticmethod
def _set_resource_limit(kind: int, value: int) -> None:
import resource
_, hard = resource.getrlimit(kind)
limit = value if hard == resource.RLIM_INFINITY else min(value, hard)
resource.setrlimit(kind, (limit, limit))
def _validate_output_size(self, stdout: str, stderr: str) -> None:
output_size = len((stdout or "").encode("utf-8")) + len((stderr or "").encode("utf-8"))
if output_size > self.max_output_bytes:
raise RuntimeError(f"Local execution output exceeded {self.max_output_bytes} bytes.")
def _collect_artifacts(self, artifacts_dir: Path) -> list[dict[str, Any]]:
artifacts: list[dict[str, Any]] = []
for path in sorted(artifacts_dir.rglob("*")):
if path.is_symlink():
raise RuntimeError(f"Artifact symlinks are not allowed: {path.name}")
if path.is_dir():
continue
if not path.is_file():
raise RuntimeError(f"Unsupported artifact entry: {path.name}")
if len(artifacts) >= self.max_artifacts:
raise RuntimeError(f"Local execution produced more than {self.max_artifacts} artifacts.")
size = path.stat().st_size
if size > self.max_artifact_bytes:
raise RuntimeError(f"Artifact exceeds {self.max_artifact_bytes} bytes: {path.name}")
ext = path.suffix.lower()
if ext not in ALLOWED_ARTIFACT_EXTENSIONS:
raise RuntimeError(f"Unsupported artifact type: {path.name}")
artifacts.append(
{View on GitHub (pinned to 554fb1133a)
Solutions
- Change the executed code to write real files (or copy content) into artifacts/ instead of creating symlinks: shutil.copyfile instead of os.symlink.
- If you control the prompt/template, instruct the code-generation step never to use os.symlink in artifact paths.
- Do not treat this as a bug to bypass — it is a security guard; if you need a file from outside artifacts/, have the code read it and write a copy into artifacts/.
- Run untrusted code under self_managed provider with a real container boundary instead of LocalProvider.
Example fix
# before (executed code)
os.symlink('/data/report.csv', 'artifacts/report.csv')
# after (executed code)
import shutil
shutil.copyfile('/data/report.csv', 'artifacts/report.csv') Defensive patterns
Strategy: validation
Try / catch
try:
result = provider.execute_code(instance_id, code, "python")
except RuntimeError as e:
if "symlinks are not allowed" in str(e):
# code tried os.symlink in artifacts/; regenerate/fix code to copy files
raise SuspiciousArtifact(str(e)) from e
raise Prevention
- Never create symlinks inside the artifacts directory in executed code; copy files instead.
- Treat this error as a security signal, not a bug — audit the generated code.
- Run truly untrusted code under the containerized self_managed provider.
When it happens
Trigger: Executed code creating a symlink inside its artifacts directory, e.g. os.symlink('/etc/passwd', 'artifacts/leak.csv') or ln -s ../../big.bin artifacts/data.bin. Triggered during execute_code()'s artifact collection phase, after the process exits.
Common situations: LLM-generated code 'organizing' outputs with symlinks; adversarial code deliberately probing the local provider (which the class docstring warns is not a real sandbox boundary); a library that writes symlinked convenience files into the working tree.
Related errors
- Artifact symlinks are not allowed: {relative_path}
- SANDBOX_LOCAL_MAX_ARTIFACTS must be greater than or equal to
- SANDBOX_LOCAL_MAX_ARTIFACT_BYTES must be greater than 0.
- Unsupported artifact entry: {path.name}
- Local execution produced more than {self.max_artifacts} arti
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/70c362f4d5894eba.
Report an issue: GitHub.