{"record":{"id":"e22ca8499e9e4ae0","repo":"infiniflow/ragflow","slug":"ssh-execution-produced-more-than-self-max-artifac","errorCode":null,"errorMessage":"SSH execution produced more than {self.max_artifacts} artifacts.","messagePattern":"SSH execution produced more than (.+?) artifacts\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"agent/sandbox/providers/ssh.py","lineNumber":649,"sourceCode":"            name = entry.filename\n            remote_path = posixpath.join(current_dir, name)\n            relative_path = posixpath.join(relative_dir, name) if relative_dir else name\n            mode = entry.st_mode\n            if mode is None:\n                mode = sftp.lstat(remote_path).st_mode\n            if mode is None:\n                raise RuntimeError(f\"Unable to determine artifact entry type: {relative_path}\")\n\n            if stat.S_ISLNK(mode):\n                raise RuntimeError(f\"Artifact symlinks are not allowed: {relative_path}\")\n            if stat.S_ISDIR(mode):\n                self._collect_artifacts_recursive(sftp, remote_path, relative_path, artifacts)\n                continue\n            if not stat.S_ISREG(mode):\n                raise RuntimeError(f\"Unsupported artifact entry: {relative_path}\")\n\n            if len(artifacts) >= self.max_artifacts:\n                raise RuntimeError(f\"SSH execution produced more than {self.max_artifacts} artifacts.\")\n\n            size = int(entry.st_size or 0)\n            if size > self.max_artifact_bytes:\n                raise RuntimeError(f\"Artifact exceeds {self.max_artifact_bytes} bytes: {relative_path}\")\n\n            ext = os.path.splitext(name)[1].lower()\n            if ext not in ALLOWED_ARTIFACT_EXTENSIONS:\n                raise RuntimeError(f\"Unsupported artifact type: {relative_path}\")\n\n            with sftp.file(remote_path, \"rb\") as artifact_file:\n                content = artifact_file.read()\n\n            artifacts.append(\n                {\n                    \"name\": relative_path,\n                    \"content_b64\": base64.b64encode(content).decode(\"ascii\"),\n                    \"mime_type\": mimetypes.guess_type(name)[0] or \"application/octet-stream\",\n                    \"size\": size,","sourceCodeStart":631,"sourceCodeEnd":667,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/agent/sandbox/providers/ssh.py#L631-L667","documentation":"Raised as RuntimeError during artifact collection when the number of already-collected artifacts reaches self.max_artifacts (default 20, configurable) and another regular file is found. It caps artifact count to bound response size and base64 encoding cost. The check fires before each append, so execution itself succeeded — only the artifact harvest is refused.","triggerScenarios":"Sandboxed code writing more than 20 files into the artifacts directory (plots per category, per-page images, chunked exports); lowering max_artifacts in config below what the workflow normally emits; a runtime dropping many cache files into the collected tree.","commonSituations":"Chart-generation agents producing one PNG per series; scraping pipelines saving dozens of fragments; defaults too tight for batch report generation.","solutions":["Write only the artifacts you need: delete intermediates before returning, or consolidate into one archive/file","Raise the cap at initialize: config {'max_artifacts': 100} if the caller can handle the payload","Keep package-manager/runtime caches out of the artifacts directory"],"exampleFix":"# before (sandboxed code): 50 pngs written\nfor i in range(50): plt.savefig(f'artifacts/p{i}.png')\n\n# after: keep top 3\nfor i in range(3): plt.savefig(f'artifacts/p{i}.png')","handlingStrategy":"validation","validationCode":"import os\nfiles = [p for p in os.listdir(artifacts_dir) if os.path.isfile(os.path.join(artifacts_dir, p))]\nif len(files) > provider.max_artifacts:\n    raise RuntimeError(f\"{len(files)} artifacts exceed limit {provider.max_artifacts}; prune before returning\")","typeGuard":null,"tryCatchPattern":"try:\n    artifacts = provider.collect_artifacts(instance_id, artifacts_dir)\nexcept RuntimeError as e:\n    if \"more than\" in str(e) and \"artifacts\" in str(e):\n        raise RuntimeError(\"too many output files; keep only essential artifacts\") from e","preventionTips":["Write only deliverables into the artifacts dir; clean intermediates","Set max_artifacts at initialize to match your workflow's real output cardinality","Consolidate many small outputs into a single JSON/CSV when possible"],"tags":["artifacts","limits","validation","ssh"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}