{"record":{"id":"d9ca599800acc76c","repo":"microsoft/semantic-kernel","slug":"an-output-target-has-already-been-set-d9ca59","errorCode":null,"errorMessage":"An output target has already been set.","messagePattern":"An output target has already been set\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/Experimental/Process.Core/ProcessStepEdgeBuilder.cs","lineNumber":118,"sourceCode":"    public ProcessStepEdgeBuilder OnCondition(KernelProcessEdgeCondition condition)\n    {\n        Verify.NotNull(condition, nameof(condition));\n        this.Condition = condition;\n        return this;\n    }\n\n    /// <summary>\n    /// Internally overridable implementation: Signals that the output of the source step should be sent to the specified target when the associated event fires.\n    /// </summary>\n    /// <param name=\"target\">The output target.</param>\n    /// <returns>A fresh builder instance for fluid definition</returns>\n    /// <exception cref=\"InvalidOperationException\"></exception>\n    /// <exception cref=\"ArgumentException\"></exception>\n    internal virtual ProcessStepEdgeBuilder SendEventTo_Internal(ProcessTargetBuilder target)\n    {\n        if (this.Target is not null)\n        {\n            throw new InvalidOperationException(\"An output target has already been set.\");\n        }\n\n        if (target is ProcessFunctionTargetBuilder functionTargetBuilder)\n        {\n            if (functionTargetBuilder.Step is ProcessMapBuilder && this.Source is ProcessMapBuilder)\n            {\n                throw new ArgumentException($\"{nameof(ProcessMapBuilder)} may not target another {nameof(ProcessMapBuilder)}.\", nameof(target));\n            }\n        }\n\n        this.Target = target;\n        this.Source.LinkTo(this.EventData.EventId, this);\n\n        return new ProcessStepEdgeBuilder(this.Source, this.EventData.EventId, this.EventData.EventName, this.EdgeGroupBuilder, this.Condition);\n    }\n\n    /// <summary>\n    /// Emit the SK step event as an external event with specific topic name","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/dotnet/src/Experimental/Process.Core/ProcessStepEdgeBuilder.cs#L100-L136","documentation":"Thrown by ProcessStepEdgeBuilder.SendEventTo_Internal when assigning a target to an edge that already has one. Each edge is single-target by design; SendEventTo_Internal returns a fresh builder for fluent chaining, and reusing the already-targeted edge instance is treated as a programming error.","triggerScenarios":"Calling SendEventTo twice on the same ProcessStepEdgeBuilder instance instead of chaining on the returned fresh builder; or capturing and reusing an edge reference after it has been assigned a target.","commonSituations":"Storing the edge from OnEvent(...) in a variable and calling SendEventTo on it in two branches; refactor that inadvertently double-targets; misusing the fluent API by ignoring the returned builder.","solutions":["Always chain SendEventTo on the builder returned by the previous call rather than the original edge variable.","If you need one event to reach multiple targets, define separate edges (one OnEvent/SendEventTo per target) or use an edge group/fan-out mechanism.","Refactor stored edge references so each is targeted exactly once."],"exampleFix":"// before: same edge targeted twice\nvar e = step.OnEvent(\"Done\");\ne.SendEventTo(a);\ne.SendEventTo(b); // throws\n\n// after: chain fresh builders\nstep.OnEvent(\"Done\").SendEventTo(a);\nstep.OnEvent(\"Done\").SendEventTo(b);","handlingStrategy":"validation","validationCode":"if (edge.Target is not null)\n    throw new InvalidOperationException(\"Edge already has a target; create a new edge for additional targets.\");","typeGuard":"static bool EdgeIsUntargeted(ProcessStepEdgeBuilder e) => e.Target is null;","tryCatchPattern":"try { edge.SendEventTo(target); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"already been set\"))\n{ /* use a fresh edge from OnEvent for the new target */ }","preventionTips":["Treat ProcessStepEdgeBuilder instances as single-use after a target is set.","Chain on the builder returned by SendEventTo, not the captured variable.","For fan-out, create one edge per target from the same source event."],"tags":["semantic-kernel","process","edge","builder"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}