zylon-ai/private-gpt · error · RuntimeError

Sandbox not started. Call start() first.

Error message

Sandbox not started. Call start() first.

What it means

Guard inside _exec_code(): generated Python is executed remotely through self._client, and a None client means the sandbox session was never started (or was stopped). This is a lifecycle-order error — the adapter is stateful and _exec_code is only valid between start() and stop().

Source

Thrown at private_gpt/components/tabular/pandasai_sandbox.py:273

        """
        ).strip()

        try:
            result = self._run(
                self._client.run_code(setup_code, SandboxCodeOptions(language="python"))
            )
            if not result.success:
                logger.warning("Environment setup warning: %s", result.error)
        except Exception as e:
            logger.warning("Error during environment setup: %s", e)

    # ------------------------------------------------------------------
    # Code execution
    # ------------------------------------------------------------------

    def _exec_code(self, code: str, environment: dict[str, Any]) -> dict[str, Any]:
        if not self._client:
            raise RuntimeError("Sandbox not started. Call start() first.")

        try:
            sql_queries = self._extract_sql_queries_from_code(code)
            datasets_code, exceptions = self._process_sql_queries(
                sql_queries, environment
            )
            if exceptions:
                raise ValueError(
                    f"Failed to execute some SQL queries: "
                    f"{', '.join(str(e) for e in exceptions)}"
                )

            processed_code = self._prepare_code_for_execution(code)
            full_code = "\n\n".join(
                part for part in (self._PREAMBLE, datasets_code, processed_code) if part
            )

            execution_result = self._run(

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Ensure start() is called (and succeeded) before any code execution; PandasAIService._chat_with_sandbox already does this — reuse that path.
  2. Do not share one adapter across concurrent requests without a lock; stop() from one request nulls _client for all.
  3. After any start() failure, discard the adapter instance instead of reusing it.
  4. If hitting this in tests, wrap execution with a context manager that starts/stops the sandbox.

Example fix

# before
sandbox = PandasAISandboxAdapter(client=client, ...)
sandbox._exec_code(code, env)  # RuntimeError

# after
from contextlib import contextmanager

@contextmanager
def started(sandbox):
    sandbox.start()
    try:
        yield sandbox
    finally:
        sandbox.stop()

with started(sandbox):
    sandbox._exec_code(code, env)
Defensive patterns

Strategy: validation

Validate before calling

def is_started(sandbox) -> bool:
    return sandbox._started and sandbox._client is not None

# guard every execution path:
# if not is_started(sandbox): sandbox.start()

Prevention

When it happens

Trigger: Calling chat()/execution flows with sandbox=None vs a not-yet-started adapter; executing code after stop() ran (stop() sets _client=None at line 185); a start() failure earlier in the request leaving the adapter in a dead state.

Common situations: Missing sandbox.start() in a new integration path; exception in a prior request leaving the adapter stopped but still cached; concurrency where one request stops the shared adapter while another is mid-flight.

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/850427b4f79c6df1. Report an issue: GitHub.