microsoft/aspire · error · InvalidOperationException
Step ' ' not found in pipeline. Available steps
Error message
Step '{stepName}' not found in pipeline. Available steps: {availableSteps} What it means
When executing a single step sequentially (ExecuteStepSequentiallyAsync), the pipeline resolves the requested stepName against all registered steps using an ordinal dictionary. If the name does not match any step, it throws this InvalidOperationException and helpfully lists the available step names so the caller can see what went wrong. It is a lookup failure, not a state corruption.
Solutions
- Compare stepName against the available steps list printed in the exception message and correct the spelling/casing.
- Enumerate registered steps (pipeline.Steps or equivalent) at runtime and pick the name programmatically instead of hardcoding it.
- Ensure the step is added before execution: verify AddStep was called for the target step on this pipeline instance.
- Normalize name comparison: pass exactly the step.Name value rather than a re-typed literal.
Example fix
// before await pipeline.ExecuteStepSequentiallyAsync(context, "depoy"); // typo // after var stepName = pipeline.Steps.First(s => s.Name == "deploy").Name; await pipeline.ExecuteStepSequentiallyAsync(context, stepName);
Defensive patterns
Strategy: validation
Validate before calling
var known = pipeline.Steps.Select(s => s.Name).ToHashSet(StringComparer.Ordinal);
if (!known.Contains(stepName))
{
throw new InvalidOperationException($"'{stepName}' is not a registered step. Known: {string.Join(", ", known)}");
} Try / catch
try
{
await pipeline.ExecuteStepSequentiallyAsync(context, stepName);
}
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Step '") && ex.Message.Contains("not found in pipeline"))
{
logger.LogError(ex, "Unknown step '{StepName}'. Register it or fix the name.", stepName);
} Prevention
- Reference step names via constants/shared fields, never inline literals.
- Match names case-sensitively (lookup is ordinal).
- Trim and normalize names sourced from CLI args or config.
- After renaming a step, grep for its old name across scripts and tests.
When it happens
Trigger: Calling the pipeline execution overload that targets a specific step name with a name that is misspelled, differs in casing, contains extra whitespace, or refers to a step that was never added (or was added under a different name).
Common situations: Running 'aspire publish/run --step <name>' style targeting from a script or CLI with a stale step name; renaming a step in code but not updating the caller; case-sensitivity confusion ('Deploy' vs 'deploy'); typos in test code invoking ExecuteStepSequentiallyAsync.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- A step with the name
- Step ' ' is required by unknown step
- Step ' ' not found in pipeline. Available steps
- The requiredBy parameter must be a string or IEnumerable
- Array params contains empty item
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/64f75f78ba47a95a.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting/Pipelines/DistributedApplicationPipeline.cs:608
/// <param name="context">The pipeline context for execution.</param>
/// <returns>A task representing the asynchronous operation.</returns>
internal async Task ExecuteStepSequentiallyAsync(
string stepName,
PipelineContext context)
{
var allSteps = await ResolveStepsAsync(context).ConfigureAwait(false);
if (allSteps.Count == 0)
{
return;
}
var allStepsByName = allSteps.ToDictionary(s => s.Name, StringComparer.Ordinal);
if (!allStepsByName.TryGetValue(stepName, out var targetStep))
{
var availableSteps = string.Join(", ", allSteps.Select(s => $"'{s.Name}'"));
throw new InvalidOperationException(
$"Step '{stepName}' not found in pipeline. Available steps: {availableSteps}");
}
var stepsToExecute = ComputeTransitiveDependencies(targetStep, allStepsByName);
await ExecuteStepsSequentially(stepsToExecute, context).ConfigureAwait(false);
}
/// <summary>
/// Resolves all pipeline steps (from built-in steps and resource annotations),
/// normalizes RequiredBy relationships to DependsOn, and validates the steps
/// without executing them. The returned list is in collection order; use
/// <see cref="GetTopologicalOrder"/> to obtain execution order.
/// </summary>
internal async Task<List<PipelineStep>> ResolveStepsAsync(PipelineContext context)
{
var annotationSteps = await CollectStepsFromAnnotationsAsync(context).ConfigureAwait(false);
// Configuration callbacks are run on every resolution, so give them fresh built-in steps insteadView on GitHub (pinned to 25830f84bd)