infiniflow/ragflow · error · RuntimeError

Unsupported artifact type: {relative_path}

Error message

Unsupported artifact type: {relative_path}

What it means

Raised during artifact collection when an artifact file's extension is not in ALLOWED_ARTIFACT_EXTENSIONS ({.csv, .html, .jpeg, .jpg, .json, .pdf, .png, .svg}). The allowlist constrains what untrusted code can push back into the agent context; any other file type aborts collection.

Source

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

            # 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(
                {
                    "name": relative_path,
                    "content_b64": base64.b64encode(content).decode("ascii"),
                    "mime_type": mimetypes.guess_type(name)[0] or "application/octet-stream",
                    "size": size,
                }
            )

    def _safe_terminate(self, sandbox) -> None:
        # Best-effort: max_lifetime reclaims the sandbox if this fails.
        try:
            sandbox.terminate()
        except Exception as exc:
            logger.warning("Failed to terminate Tenki sandbox, relying on max_lifetime: %s", exc)

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Have the script write only allowlisted types into artifacts/ and keep other files elsewhere in the work dir.
  2. Convert outputs: serialize objects as .json, tabular data as .csv, render plots as .png/.svg.
  3. If a type is genuinely required, extend ALLOWED_ARTIFACT_EXTENSIONS after reviewing the security implications for untrusted code.

Example fix

# before
# script: open('artifacts/log.txt','w').write(log)  # .txt rejected

# after
# script: json.dump({'log': log}, open('artifacts/log.json','w'))
Defensive patterns

Strategy: type-guard

Validate before calling

ALLOWED = {".csv", ".html", ".jpeg", ".jpg", ".json", ".pdf", ".png", ".svg"}
# in generated code, verify before finishing:
# for p in pathlib.Path('artifacts').rglob('*'):
#     assert p.suffix.lower() in ALLOWED, p

Type guard

def is_allowed_artifact(name: str) -> bool:
    return os.path.splitext(name)[1].lower() in {".csv", ".html", ".jpeg", ".jpg", ".json", ".pdf", ".png", ".svg"}

Prevention

When it happens

Trigger: Script writes files like result.txt, model.pkl, data.parquet, or backup~ into artifacts/; the extension check (case-insensitive via lower()) rejects them before read_bytes().

Common situations: Generated code saving logs, pickles, or arbitrary working files into the artifacts dir by convention; tooling that assumes artifacts/ is a general output bucket.

Related errors


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