infiniflow/ragflow · error · RuntimeError

Artifact directory nesting exceeds {MAX_ARTIFACT_DEPTH} leve

Error message

Artifact directory nesting exceeds {MAX_ARTIFACT_DEPTH} levels: {relative_dir}

What it means

Raised during artifact collection when the recursive directory walk under artifacts/ exceeds MAX_ARTIFACT_DEPTH (16 levels). The guard exists to bound traversal of untrusted, potentially pathological directory trees (including symlink-loop constructs) produced by executed code.

Source

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

            raise RuntimeError(f"Unsupported language for Tenki provider: {language}")

        script_path = posixpath.join(remote_work_dir, script_name)
        sandbox.fs.write_text(script_path, script_content)
        return script_path, [executable, script_path]

    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"Tenki execution output exceeded {self.max_output_bytes} bytes.")

    def _collect_artifacts(self, sandbox, artifacts_dir: str) -> list[dict[str, Any]]:
        artifacts: list[dict[str, Any]] = []
        self._collect_artifacts_recursive(sandbox, artifacts_dir, "", artifacts, depth=0)
        return artifacts

    def _collect_artifacts_recursive(self, sandbox, current_dir: str, relative_dir: str, artifacts: list[dict[str, Any]], depth: int) -> None:
        if depth > MAX_ARTIFACT_DEPTH:
            raise RuntimeError(f"Artifact directory nesting exceeds {MAX_ARTIFACT_DEPTH} levels: {relative_dir}")

        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.

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Fix the script to flatten or limit artifact directory depth (keep outputs at the top level of artifacts/).
  2. Write only final result files into artifacts/, not source trees or dependency directories.
  3. If legitimate output genuinely needs more nesting, raise MAX_ARTIFACT_DEPTH — but treat depth blowups as a code smell first.

Example fix

# before
# script: for i in range(100): os.makedirs(f'artifacts/{"d/"*i}x')

# after
# script: os.makedirs('artifacts', exist_ok=True); shutil.copy(result, 'artifacts/result.csv')
Defensive patterns

Strategy: validation

Validate before calling

# in generated code, keep artifacts flat
# depth check helper the script can run:
# for root, dirs, files in os.walk('artifacts'):
#     assert root.count(os.sep) - 'artifacts'.count(os.sep) <= 16

Try / catch

try:
    result = provider.execute_code(instance_id, code)
except RuntimeError as exc:
    if "nesting exceeds" in str(exc):
        result = provider.execute_code(instance_id, flattened_artifacts_version(code))
    else:
        raise

Prevention

When it happens

Trigger: User script creates nested directories deeper than 16 levels inside the artifacts dir (e.g. a loop doing os.makedirs('a/'*100)), or a directory structure with cycles that the symlink rejection did not prune before depth accrues.

Common situations: LLM-generated code copying deep node_modules-style trees into artifacts, archive extraction with deeply nested paths, or adversarial code trying to exhaust the walker.

Related errors


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