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
- Call Build() on the fixture before accessing Services.
- Move service resolution into test setup that runs after Build().
- 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
- Call Build() in the test initialize/constructor immediately after configuration.
- Expose a helper property that builds lazily before returning services.
- Never resolve services in base-class setup that runs before fixture Build().
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
- Register with configured before calling , or call with a…
- Cannot deserialize to .
- Failed to deserialize
- Failed to convert an object of type
- Invalid value type.
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)