elsa-workflows/elsa-core · error · InvalidOperationException

Build() must be called before accessing services

Error message

Build() must be called before accessing services

What it means

WorkflowTestFixture lazily builds an IServiceProvider from its configured Elsa features. Accessing the Services property before calling Build() throws this InvalidOperationException because the underlying service container does not exist yet. The fixture is designed for explicit build-then-use ordering so all configuration actions run before service resolution.

Solutions

  1. Call Build() on the fixture before accessing Services.
  2. Move service resolution into test setup that runs after Build().
  3. Chain configuration calls then Build() in one place in the test initialize method.

Example fix

// before
var services = fixture.Services;
// after
fixture.Build();
var services = fixture.Services;
Defensive patterns

Strategy: validation

Validate before calling

if (fixture is not { } f) throw new InvalidOperationException("Fixture not initialized");
var services = fixture.TestServiceState // or simply ensure Build() ran first:
if (!built) fixture.Build();
var sp = fixture.Services;

Type guard

bool IsBuilt(WorkflowTestFixture f) => f.Services is IServiceProvider;

Try / catch

try { var sp = fixture.Services; } catch (InvalidOperationException ex) when (ex.Message.Contains("Build()")) { fixture.Build(); var sp = fixture.Services; }

Prevention

When it happens

Trigger: Accessing the fixture's Services property (directly or via helpers that resolve services) before calling Build() on the fixture.

Common situations: Test setup code that resolves services in a constructor instead of in test initialization; copy-pasted test code where the Build() call was removed or reordered; fixtures subclassed with additional setup that runs before Build().

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/a48979b4f88598a1. Report an issue: GitHub.

Appendix: source

Thrown at src/common/Elsa.Testing.Shared.Integration/WorkflowTestFixture.cs:45

    /// Initializes a new instance of the <see cref="WorkflowTestFixture"/> class.
    /// </summary>
    /// <param name="testOutputHelper">The test output helper</param>
    public WorkflowTestFixture(ITestOutputHelper testOutputHelper)
    {
        _testApplicationBuilder = new(testOutputHelper);
        CapturingTextWriter = new();
        _testApplicationBuilder.WithCapturingTextWriter(CapturingTextWriter);
    }

    /// <summary>
    /// Gets the capturing text writer that captures standard output from WriteLine activities.
    /// </summary>
    public CapturingTextWriter CapturingTextWriter { get; }

    /// <summary>
    /// Gets the service provider. Throws if Build() hasn't been called yet.
    /// </summary>
    public IServiceProvider Services => _services ?? throw new InvalidOperationException("Build() must be called before accessing services");

    /// <summary>
    /// Configures Elsa features.
    /// </summary>
    /// <param name="configure">Action to configure Elsa</param>
    /// <returns>The fixture instance for method chaining</returns>
    [UsedImplicitly]
    public WorkflowTestFixture ConfigureElsa(Action<IModule> configure)
    {
        _testApplicationBuilder.ConfigureElsa(configure);
        return this;
    }

    /// <summary>
    /// Configures the service collection.
    /// </summary>
    /// <param name="configure">Action to configure the service collection</param>
    /// <returns>The fixture instance for method chaining</returns>

View on GitHub (pinned to fe9217bdfa)