pulumi/pulumi · error · RunError

pulumi.run may only be called once

Error message

pulumi.run may only be called once

What it means

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).

Source

Thrown at sdk/python/lib/pulumi/runtime/stack.py:322

    def _run_legacy_callback(
        self, func: Callable[[], Optional[Awaitable[None]]]
    ) -> None:
        try:
            awaitable = func()
            # This _should_ be an awaitable but old pulumi executors returned modules here, so we need to handle that
            # with a type check rather than just `is not None`.
            if isawaitable(awaitable):
                _sync_await(awaitable)
        finally:
            self._finish()
            # Intentionally leave this resource installed in case subsequent async work uses it.

    def _register_async_program(self, program: _AsyncProgram) -> None:
        from ..errors import RunError

        if self._async_program is not None or self._async_program_registration_closed:
            raise RunError("pulumi.run may only be called once")

        self._async_program = program

    def _take_async_program(self) -> Optional[_AsyncProgram]:
        self._async_program_registration_closed = True
        return self._async_program

    def _add_program_outputs(self, outputs: Optional["Inputs"]) -> None:
        if outputs is None:
            return
        for name, value in outputs.items():
            export(name, value)

    def _finish(self) -> None:
        """Register this stack's outputs exactly once."""
        if self._outputs_registered:
            return

View on GitHub (pinned to 793f7b2e16)

Solutions

  1. Call pulumi.run exactly once, at the program entry point
  2. Refactor helper libraries to accept the program function instead of calling pulumi.run themselves
  3. Move late async work into the single registered program rather than a second pulumi.run call

Example fix

// before
pulumi.run(setup)
pulumi.run(teardown)  # RunError: only once
// after
async def program():
    await setup()
    await teardown()
pulumi.run(program)
Defensive patterns

Strategy: validation

Validate before calling

called = False
def register_once(program):
    global called
    if called:
        raise RuntimeError('pulumi.run already invoked')
    called = True
    pulumi.run(program)

Try / catch

from pulumi.errors import RunError
try:
    pulumi.run(program)
except RunError as e:
    if 'only be called once' in str(e):
        logger.error('Duplicate pulumi.run call — consolidate into one program')
    raise

Prevention

When it happens

Trigger: Calling pulumi.run twice within the same Pulumi program execution, or calling it after the runtime has started consuming the registered program (registration closed).

Common situations: 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).

Related errors


AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31). Data as JSON: /api/errors/b09e4ba32f94585c. Report an issue: GitHub.