agentscope-ai/agentscope · critical · RuntimeError

gateway shim produced non-JSON stdout: {result.stdout[:200]!

Error message

gateway shim produced non-JSON stdout: {result.stdout[:200]!r}

What it means

Raised by GatewayClient.exec_request() when the in-sandbox shim exits 0 but its stdout is not parseable JSON. The shim is expected to print exactly one JSON envelope ({status, body/body_file, ...}); anything else (empty stdout, warnings printed to stdout, partial output) triggers this. The first 200 bytes of stdout are shown repr-escaped for diagnosis.

Source

Thrown at src/agentscope/workspace/_gateway_client.py:678

                    timeout=self.timeout,
                )
            finally:
                if wrote_body_file is not None:
                    try:
                        await self.backend.delete_path(wrote_body_file)
                    except Exception:
                        pass

            if result.exit_code != 0:
                raise RuntimeError(
                    f"gateway shim exited with {result.exit_code}: "
                    f"{result.stderr.decode(errors='replace')[:500]}",
                )

            try:
                env = json.loads(result.stdout)
            except Exception as e:
                raise RuntimeError(
                    "gateway shim produced non-JSON stdout: "
                    f"{result.stdout[:200]!r}",
                ) from e

            status = int(env["status"])
            if status == -1:
                raise RuntimeError(
                    "gateway request failed: "
                    f"{env.get('error', 'unknown error')}",
                )

            if "body_file" in env:
                spilled = env["body_file"]
                body_bytes = await self.backend.read_file(spilled)
                try:
                    await self.backend.delete_path(spilled)
                except Exception:
                    pass

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Inspect the repr'd stdout prefix in the message — warnings or partial JSON reveal the cause
  2. Ensure the backend's exec captures stdout/stderr as separate streams (no TTY merging)
  3. Set PYTHONWARNINGS=ignore / -W ignore for the sandbox python if warnings leak to stdout
  4. Redeploy so SHIM_SCRIPT and sandbox python3 versions match
Defensive patterns

Strategy: fallback

Validate before calling

res = await backend.exec_shell(["python3", "-c", "import json;print(json.dumps({'ok':1}))"])
if not res.ok() or not res.stdout.strip().startswith(b"{"):
    raise RuntimeError("sandbox python stdout is polluted")

Try / catch

try:
    status, body = await gateway.exec_request(...)
except RuntimeError as e:
    if "non-JSON stdout" in str(e):
        logger.error("shim stdout polluted; inspect backend exec stream config")
    raise

Prevention

When it happens

Trigger: Any gateway call where the shim's stdout is empty (killed mid-print), where the sandbox merges stderr into stdout (site warnings, deprecation notices), or where a different process/echo interferes with the exec stream.

Common situations: Sandbox TTY/exec config merging stderr into stdout; PYTHONWARNINGS or import-time warnings printed to stdout; shim killed by signal producing truncated stdout; version skew between SHIM_SCRIPT and what the sandbox's python3 supports.

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/29b349ec53ced8db. Report an issue: GitHub.