{"record":{"id":"91190ab7cfda74ba","repo":"microsoft/autogen","slug":"executor-must-be-started-before-executing-cells","errorCode":null,"errorMessage":"Executor must be started before executing cells","messagePattern":"Executor must be started before executing cells","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-ext/src/autogen_ext/code_executors/jupyter/_jupyter_code_executor.py","lineNumber":246,"sourceCode":"                                path = self._save_image(content)\n                                output_files.append(path)\n                            case \"image/jpeg\":\n                                # TODO: Should this also be encoded? Images are encoded as both png and jpg\n                                pass\n                            case \"text/html\":\n                                path = self._save_html(content)\n                                output_files.append(path)\n                            case _:\n                                outputs.append(json.dumps(content))\n                case _:\n                    pass\n\n        return JupyterCodeResult(exit_code=exit_code, output=\"\\n\".join(outputs), output_files=output_files)\n\n    async def _execute_cell(self, cell: NotebookNode) -> NotebookNode:\n        # Temporary push cell to nb as async_execute_cell expects it. But then we want to remove it again as cells can take up significant amount of memory (especially with images)\n        if not self._client:\n            raise RuntimeError(\"Executor must be started before executing cells\")\n        self._client.nb.cells.append(cell)\n        output = await self._client.async_execute_cell(\n            cell,\n            cell_index=0,\n        )\n        self._client.nb.cells.pop()\n        return output\n\n    def _save_image(self, image_data_base64: str) -> Path:\n        \"\"\"Save image data to a file.\"\"\"\n        image_data = base64.b64decode(image_data_base64)\n        path = self._output_dir / f\"{uuid.uuid4().hex}.png\"\n        path.write_bytes(image_data)\n        return path.absolute()\n\n    def _save_html(self, html_data: str) -> Path:\n        \"\"\"Save HTML data to a file.\"\"\"\n        path = self._output_dir / f\"{uuid.uuid4().hex}.html\"","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-ext/src/autogen_ext/code_executors/jupyter/_jupyter_code_executor.py#L228-L264","documentation":"Raised by JupyterCodeExecutor._execute_cell when self._client is falsy, meaning the executor was never started. The executor must be running (start() called, or entered via 'async with') before execute_code_blocks is invoked.","triggerScenarios":"Calling execute_code_blocks() (or any code-execution path) on a JupyterCodeExecutor instance that did not go through await executor.start() or 'async with executor:'; using an executor after stop(); relying on an agent runtime that does not auto-start executors.","commonSituations":"Borrowing the executor for manual calls outside a runtime, forgetting 'async with' in scripts, calling execute before the runtime registered the executor (ordering bugs in custom agent loops), reusing a stopped executor.","solutions":["Call await executor.start() before executing, and prefer the context-manager form 'async with JupyterCodeExecutor() as e: ...'.","If used inside an agent runtime, attach the executor before the first code-execution turn so the runtime starts it.","Do not reuse an executor after stop(); create and start a new one."],"exampleFix":"# before\nexecutor = JupyterCodeExecutor()\nresult = await executor.execute_code_blocks([CodeBlock(code=\"1+1\", language=\"python\")], CancellationToken())\n\n# after\nasync with JupyterCodeExecutor() as executor:\n    result = await executor.execute_code_blocks([CodeBlock(code=\"1+1\", language=\"python\")], CancellationToken())","handlingStrategy":"validation","validationCode":"async def execute(executor, blocks, token):\n    if not getattr(executor, \"_started\", False):\n        await executor.start()\n    return await executor.execute_code_blocks(blocks, token)","typeGuard":"from autogen_ext.code_executors.jupyter import JupyterCodeExecutor\n\ndef is_started(executor: JupyterCodeExecutor) -> bool:\n    return getattr(executor, \"_started\", False)","tryCatchPattern":"try:\n    await executor.execute_code_blocks(blocks, token)\nexcept RuntimeError as e:\n    if \"must be started\" in str(e):\n        await executor.start()\n        result = await executor.execute_code_blocks(blocks, token)\n    else:\n        raise","preventionTips":["Prefer 'async with JupyterCodeExecutor() as executor:' so lifecycle is automatic.","Attach executors to the runtime before the first code-execution turn.","Never share one executor across sequential runs without restarting it."],"tags":["jupyter","lifecycle","executor","usage"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}