infiniflow/ragflow · error · RuntimeError

Unsupported language for UCloud Agent Sandbox provider

Error message

Unsupported language for UCloud Agent Sandbox provider: {language}

What it means

Defensive guard inside `_prepare_script`: after `execute()` normalized the language, the wrapper builder only handles exactly "python" and "nodejs". If a language slips past the create_instance whitelist (e.g. via a divergent normalization path or a directly-injected instance state), this RuntimeError fires. In practice it should be unreachable for public callers because create_instance already rejects the same set.

Solutions

  1. Pass only "python" or "nodejs" through to execute (normalize at your boundary).
  2. If maintaining a fork that adds languages, add the branch in _prepare_script (script_name, wrapper builder, executable) together with the whitelist in create_instance.
  3. Keep the two language sets in sync — extract a shared SUPPORTED_LANGUAGES constant in a patch.
  4. Report upstream if you hit it via public APIs only, since it indicates inconsistent provider state.

Example fix

# before (fork added 'ts' to the create_instance whitelist but not here)
provider.execute(inst, code, language="ts")  # RuntimeError in _prepare_script

# after
provider.execute(inst, code, language="nodejs")
Defensive patterns

Strategy: type-guard

Validate before calling

lang = str(language).strip().lower()
if lang not in {"python", "nodejs"}:
    raise ValueError(f"Unsupported language {language!r}")
result = provider.execute(instance_id, code, language=lang)

Type guard

def is_supported_ucloud_language(language: str) -> bool:
    return str(language).strip().lower() in {"python", "nodejs"}

Try / catch

try:
    result = provider.execute(instance_id, code, language=language)
except RuntimeError as e:
    if "Unsupported language" in str(e):
        raise ValueError(f"Bad language config: {language!r}") from e
    raise

Prevention

When it happens

Trigger: Calling the internal execute path with a language string that normalize does not map to python/nodejs — most realistically when instance metadata or language is supplied by custom code that bypasses create_instance validation.

Common situations: Custom orchestration storing its own language field on the instance dict; refactors that change _normalize_language (adding an alias) without updating _prepare_script; test harnesses calling _prepare_script directly.

Related errors


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

Appendix: source

Thrown at agent/sandbox/providers/ucloud_agent_sandbox.py:396

            "integration": "ragflow",
        }
        if self.api_url:
            options["api_url"] = self.api_url
        return options

    def _prepare_script(self, sandbox, remote_work_dir: str, language: str, code: str, arguments: dict[str, Any]) -> tuple[str, str]:
        """Wrap user code, upload it, and return its path and executable."""
        args_json = json.dumps(arguments, ensure_ascii=False)
        if language == "python":
            script_name = "main.py"
            script_content = build_python_wrapper(code, args_json)
            executable = "python3"
        elif language == "nodejs":
            script_name = "main.js"
            script_content = build_javascript_wrapper(code, args_json)
            executable = "node"
        else:
            raise RuntimeError(f"Unsupported language for UCloud Agent Sandbox provider: {language}")
        script_path = posixpath.join(remote_work_dir, script_name)
        sandbox.files.write(script_path, script_content, request_timeout=self.timeout)
        return script_path, executable

    def _validate_output_size(self, stdout: str, stderr: str) -> None:
        """Reject combined standard output that exceeds the configured limit."""
        output_size = len(stdout.encode("utf-8")) + len(stderr.encode("utf-8"))
        if output_size > self.max_output_bytes:
            raise RuntimeError(f"UCloud Agent Sandbox execution output exceeded {self.max_output_bytes} bytes.")

    def _collect_artifacts(self, sandbox, artifacts_dir: str) -> list[dict[str, Any]]:
        """Collect allowed files from the execution artifact directory."""
        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:
        """Traverse artifact directories while enforcing type, size, and depth limits."""

View on GitHub (pinned to 554fb1133a)