infiniflow/ragflow · error · RuntimeError

Artifact symlinks are not allowed: {relative_path}

Error message

Artifact symlinks are not allowed: {relative_path}

What it means

Raised during artifact collection when an entry under artifacts/ is a symbolic link. Symlinks are rejected as a security measure: untrusted code could link to files outside the sandbox workspace (escape) or construct cycles; the check uses entry.is_symlink when populated plus the stat mode bits (stat.S_ISLNK) as the reliable fallback.

Source

Thrown at agent/sandbox/providers/tenki.py:466

        errors = self._tenki_errors()
        try:
            entries = sandbox.fs.list(current_dir)
        except errors.FileNotFoundError:
            return
        except FileNotFoundError:
            return

        # fs.list returns each entry's basename in `.path`, not an absolute
        # path, so join it onto the directory being listed.
        for entry in sorted(entries, key=lambda item: item.path):
            name = posixpath.basename(entry.path)
            remote_path = posixpath.join(current_dir, name)
            relative_path = posixpath.join(relative_dir, name) if relative_dir else name

            # Reject symlinks. `is_symlink` is not populated by every SDK
            # release, so also inspect the stat mode bits as the reliable check.
            if getattr(entry, "is_symlink", False) or stat.S_ISLNK(entry.mode or 0):
                raise RuntimeError(f"Artifact symlinks are not allowed: {relative_path}")
            if entry.is_dir:
                self._collect_artifacts_recursive(sandbox, remote_path, relative_path, artifacts, depth + 1)
                continue

            if len(artifacts) >= self.max_artifacts:
                raise RuntimeError(f"Tenki execution produced more than {self.max_artifacts} artifacts.")

            size = int(entry.size or 0)
            if size > self.max_artifact_bytes:
                raise RuntimeError(f"Artifact exceeds {self.max_artifact_bytes} bytes: {relative_path}")

            ext = os.path.splitext(name)[1].lower()
            if ext not in ALLOWED_ARTIFACT_EXTENSIONS:
                raise RuntimeError(f"Unsupported artifact type: {relative_path}")

            content = sandbox.fs.read_bytes(remote_path)
            artifacts.append(
                {

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Change the script to copy real files (shutil.copy) into artifacts/ instead of symlinking.
  2. Treat this error in agent flows as a suspicious-code signal: reject or sanitize the generated program.
  3. Do not attempt to whitelist specific link targets — the provider rejects all symlinks by design.

Example fix

# before
# script: os.symlink('/etc/passwd', 'artifacts/passwd')

# after
# script: shutil.copyfile('/etc/passwd', 'artifacts/passwd')  # only if that file is legitimately accessible
Defensive patterns

Strategy: try-catch

Validate before calling

# in generated code, never place symlinks in artifacts:
# assert no os.path.islink(p) for p in pathlib.Path('artifacts').rglob('*')

Try / catch

try:
    result = provider.execute_code(instance_id, code)
except RuntimeError as exc:
    if "symlinks are not allowed" in str(exc):
        flag_untrusted_code(code)  # potential sandbox escape attempt
    raise

Prevention

When it happens

Trigger: Executed script runs os.symlink('/etc/passwd', 'artifacts/leak') or creates symlink cycles inside the artifacts directory; artifact collection then aborts before reading any linked target.

Common situations: Adversarial or prompt-injected code trying to exfiltrate sandbox-external files via artifacts; build-style scripts that legitimately create symlinks (npm link patterns) and are surprised by the rejection.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/5a57e6045cdd6f8c. Report an issue: GitHub.