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
- Have the script write only allowlisted types into artifacts/ and keep other files elsewhere in the work dir.
- Convert outputs: serialize objects as .json, tabular data as .csv, render plots as .png/.svg.
- 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
- Constrain generated code to emit only allowlisted file types into artifacts/.
- Convert arbitrary outputs (logs, pickles) to .json or .csv in the script.
- Keep scratch files outside artifacts/ — only final deliverables go there.
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
- Unsupported artifact type: {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 type: {path.name}
- SSH execution produced more than {self.max_artifacts} artifa
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/554141814f47a474.
Report an issue: GitHub.