{"record":{"id":"4fb1c1a370a0e9bd","repo":"microsoft/aspire","slug":"step-step-name-is-required-by-unknown-step-requiredbystep","errorCode":null,"errorMessage":"Step '{step.Name}' is required by unknown step '{requiredByStep}'","messagePattern":"Step '(.+?)' is required by unknown step '(.+?)'","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/Pipelines/DistributedApplicationPipeline.cs","lineNumber":665,"sourceCode":"\n        return allSteps;\n    }\n\n    /// <summary>\n    /// Converts all RequiredBy relationships to their equivalent DependsOn relationships.\n    /// If step A is required by step B, this adds step A as a dependency of step B.\n    /// </summary>\n    private static void NormalizeRequiredByToDependsOn(\n        List<PipelineStep> steps,\n        Dictionary<string, PipelineStep> stepsByName)\n    {\n        foreach (var step in steps)\n        {\n            foreach (var requiredByStep in step.RequiredBySteps)\n            {\n                if (!stepsByName.TryGetValue(requiredByStep, out var requiredByStepObj))\n                {\n                    throw new InvalidOperationException(\n                        $\"Step '{step.Name}' is required by unknown step '{requiredByStep}'\");\n                }\n\n                // Add the inverse relationship: if step A is required by step B,\n                // then step B depends on step A\n                if (!requiredByStepObj.DependsOnSteps.Contains(step.Name))\n                {\n                    requiredByStepObj.DependsOnSteps.Add(step.Name);\n                }\n            }\n        }\n    }\n\n    private static (List<PipelineStep> StepsToExecute, Dictionary<string, PipelineStep> StepsByName) FilterStepsForExecution(\n        List<PipelineStep> allSteps,\n        PipelineContext context)\n    {\n        var pipelineOptions = context.Services.GetService<Microsoft.Extensions.Options.IOptions<PipelineOptions>>();","sourceCodeStart":647,"sourceCodeEnd":683,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/Pipelines/DistributedApplicationPipeline.cs#L647-L683","documentation":"Before execution, NormalizeRequiredByToDependsOn converts every step's RequiredBySteps (inverse relationships) into forward DependsOnSteps entries. If a step declares RequiredBy(\"SomeStep\") but no step named SomeStep exists in the pipeline, the graph would be left dangling, so the pipeline throws this InvalidOperationException naming both the declaring step and the unknown required-by step.","triggerScenarios":"Calling step.RequiredBy(\"name\") (directly or via AddStep's requiredBy parameter) for a step name that is never registered on the same pipeline - e.g., the required-by step was added to a different pipeline, renamed, or conditionally registered in a path that did not run.","commonSituations":"Modular pipelines where feature A declares RequiredBy(\"deploy\") assuming another module adds 'deploy', but that module was disabled; typos in requiredBy strings; steps renamed during refactoring while RequiredBy strings stayed stale; ordering issues where RequiredBy is registered before the conditional AddStep of the other step (the check happens at execution, so absence - not order - triggers it).","solutions":["Ensure a step with the exact requiredBy name is added to the same pipeline (check spelling against the registered step names).","Remove or guard the RequiredBy call when the dependency step is optional/conditional.","Centralize step-name constants (e.g., a static class of known step names) so RequiredBy and AddStep reference the same values.","Audit conditional registration paths so that any step referenced via RequiredBy is registered unconditionally or its dependents are too."],"exampleFix":"// before\npipeline.AddStep(buildStep);\nbuildStep.RequiredBy(\"publish\"); // 'publish' never added\n// after\npipeline.AddStep(buildStep);\npipeline.AddStep(new PipelineStep(\"publish\"));\nbuildStep.RequiredBy(\"publish\");","handlingStrategy":"validation","validationCode":"var declared = steps.SelectMany(s => s.RequiredBySteps).Distinct();\nvar known = steps.Select(s => s.Name).ToHashSet(StringComparer.Ordinal);\nvar unknown = declared.Where(n => !known.Contains(n)).ToList();\nif (unknown.Count > 0)\n{\n    throw new InvalidOperationException($\"RequiredBy references unknown steps: {string.Join(\", \", unknown)}\");\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    await pipeline.ExecuteAsync(context);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"is required by unknown step\"))\n{\n    logger.LogError(ex, \"A RequiredBy declaration names a step that was never added to the pipeline.\");\n}","preventionTips":["Define step names in a shared constants class used by both AddStep and RequiredBy.","Never declare RequiredBy against a step that is conditionally registered.","Validate the full graph (declared vs registered names) in tests before execution.","When renaming a step, update all RequiredBy references in the same change."],"tags":["dependency-graph","pipeline","unknown-identifier","csharp"],"backgroundTag":"missing-dependency","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}