{"record":{"id":"65bdea6e8fbf1e93","repo":"microsoft/aspire","slug":"cannot-create-task-for-step-step-id-because-the-step-is","errorCode":null,"errorMessage":"Cannot create task for step '{step.Id}' because the step is already complete.","messagePattern":"Cannot create task for step '(.+?)' because the step is already complete\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/Pipelines/PipelineActivityReporter.cs","lineNumber":93,"sourceCode":"            Data = CreateStepActivityData(step, step.Title, CompletionState.InProgress, enableMarkdown: false)\n        };\n\n        await ActivityItemUpdated.Writer.WriteAsync(state, cancellationToken).ConfigureAwait(false);\n        return step;\n    }\n\n    public async Task<ReportingTask> CreateTaskAsync(ReportingStep step, string statusText, bool enableMarkdown, CancellationToken cancellationToken)\n    {\n        if (!_steps.TryGetValue(step.Id, out var parentStep))\n        {\n            throw new InvalidOperationException($\"Step with ID '{step.Id}' does not exist.\");\n        }\n\n        lock (parentStep)\n        {\n            if (parentStep.CompletionState != CompletionState.InProgress)\n            {\n                throw new InvalidOperationException($\"Cannot create task for step '{step.Id}' because the step is already complete.\");\n            }\n        }\n\n        var task = new ReportingTask(Guid.NewGuid().ToString(), step.Id, statusText, parentStep);\n\n        // Add task to parent step\n        parentStep.AddTask(task);\n\n        var state = new PublishingActivity\n        {\n            Type = PublishingActivityTypes.Task,\n            Data = new PublishingActivityData\n            {\n                Id = task.Id,\n                StatusText = statusText,\n                CompletionState = ToBackchannelCompletionState(CompletionState.InProgress),\n                StepId = step.Id,\n                EnableMarkdown = enableMarkdown","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/Pipelines/PipelineActivityReporter.cs#L75-L111","documentation":"After locating the parent step, CreateTaskAsync checks the step's CompletionState under a lock. A task can only be attached to a step that is still InProgress; if the step already completed (Completed/WithError/WithWarning), the reporter throws InvalidOperationException because a late task could no longer affect the reported outcome.","triggerScenarios":"Calling CreateTaskAsync for a step after CompleteStepAsync (or equivalent completion) has run on it — e.g. firing concurrent tasks after awaiting step completion, or retrying task creation after a failure path already completed the step.","commonSituations":"Parallel pipeline callbacks where one callback completes the step while another still tries to add a task; a step completed in an error path before logging tasks are created; tests running steps to completion before asserting task output.","solutions":["Create all tasks before completing the step; restructure so completion is the last operation.","Check step.CompletionState == CompletionState.InProgress before calling CreateTaskAsync and skip/report accordingly.","Serialize work: await task creation before calling CompleteStepAsync instead of fire-and-forget.","Wrap in try-catch on InvalidOperationException if late task creation is benign in your flow."],"exampleFix":"// before\nawait step.CompleteAsync();\nawait reporter.CreateTaskAsync(step, text, false); // throws\n// after\nawait reporter.CreateTaskAsync(step, text, false);\nawait step.CompleteAsync();","handlingStrategy":"validation","validationCode":"if (step.CompletionState != CompletionState.InProgress)\n    return; // or log — cannot attach tasks to a completed step","typeGuard":"bool CanAttachTask(ReportingStep step) => step.CompletionState == CompletionState.InProgress;","tryCatchPattern":"try { await reporter.CreateTaskAsync(step, text, false); } catch (InvalidOperationException ex) when (ex.Message.Contains(\"already complete\")) { logger.LogDebug(\"Step completed before task creation; skipping.\"); }","preventionTips":["Complete the step only after all task creation and updates finish","Avoid fire-and-forget work that outlives step completion","Use try/finally so error paths still create diagnostics tasks before completing","Serialize step completion and task creation on one logical flow"],"tags":["reporting","lifecycle","race-condition"],"backgroundTag":"invalid-state-transition","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}