{"record":{"id":"b09e4ba32f94585c","repo":"pulumi/pulumi","slug":"pulumi-run-may-only-be-called-once","errorCode":null,"errorMessage":"pulumi.run may only be called once","messagePattern":"pulumi\\.run may only be called once","errorType":"exception","errorClass":"RunError","httpStatus":null,"severity":"error","filePath":"sdk/python/lib/pulumi/runtime/stack.py","lineNumber":322,"sourceCode":"\n    def _run_legacy_callback(\n        self, func: Callable[[], Optional[Awaitable[None]]]\n    ) -> None:\n        try:\n            awaitable = func()\n            # This _should_ be an awaitable but old pulumi executors returned modules here, so we need to handle that\n            # with a type check rather than just `is not None`.\n            if isawaitable(awaitable):\n                _sync_await(awaitable)\n        finally:\n            self._finish()\n            # Intentionally leave this resource installed in case subsequent async work uses it.\n\n    def _register_async_program(self, program: _AsyncProgram) -> None:\n        from ..errors import RunError\n\n        if self._async_program is not None or self._async_program_registration_closed:\n            raise RunError(\"pulumi.run may only be called once\")\n\n        self._async_program = program\n\n    def _take_async_program(self) -> Optional[_AsyncProgram]:\n        self._async_program_registration_closed = True\n        return self._async_program\n\n    def _add_program_outputs(self, outputs: Optional[\"Inputs\"]) -> None:\n        if outputs is None:\n            return\n        for name, value in outputs.items():\n            export(name, value)\n\n    def _finish(self) -> None:\n        \"\"\"Register this stack's outputs exactly once.\"\"\"\n        if self._outputs_registered:\n            return\n","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/pulumi/pulumi/blob/793f7b2e160db4321fb7fb6b0607461e01cb251e/sdk/python/lib/pulumi/runtime/stack.py#L304-L340","documentation":"A root Stack can host only one async program registered via pulumi.run. _register_async_program raises RunError if a program is already registered or the registration window has closed (the engine has already taken the program for execution).","triggerScenarios":"Calling pulumi.run twice within the same Pulumi program execution, or calling it after the runtime has started consuming the registered program (registration closed).","commonSituations":"Calling pulumi.run both at module top level and inside main(); a helper library that internally calls pulumi.run while the user's program also does; calling pulumi.run late (e.g. in a callback that fires after the engine consumed the program).","solutions":["Call pulumi.run exactly once, at the program entry point","Refactor helper libraries to accept the program function instead of calling pulumi.run themselves","Move late async work into the single registered program rather than a second pulumi.run call"],"exampleFix":"// before\npulumi.run(setup)\npulumi.run(teardown)  # RunError: only once\n// after\nasync def program():\n    await setup()\n    await teardown()\npulumi.run(program)","handlingStrategy":"validation","validationCode":"called = False\ndef register_once(program):\n    global called\n    if called:\n        raise RuntimeError('pulumi.run already invoked')\n    called = True\n    pulumi.run(program)","typeGuard":null,"tryCatchPattern":"from pulumi.errors import RunError\ntry:\n    pulumi.run(program)\nexcept RunError as e:\n    if 'only be called once' in str(e):\n        logger.error('Duplicate pulumi.run call — consolidate into one program')\n    raise","preventionTips":["Call pulumi.run exactly once per process, at the entry point","Ensure third-party helpers don't call pulumi.run internally","Put late async work inside the single registered program"],"tags":["python","pulumi-run","lifecycle"],"backgroundTag":"pulumi-run-outside-program","analyzedSha":"793f7b2e160db4321fb7fb6b0607461e01cb251e","analyzedAt":"2026-08-31T09:36:43.099Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}