{"record":{"id":"26bba7996f208b4c","repo":"microsoft/aspire","slug":"circular-loop-detected-for-span-span-spanid-with-parent-span","errorCode":null,"errorMessage":"Circular loop detected for span '{span.SpanId}' with parent '{span.ParentSpanId}'.","messagePattern":"Circular loop detected for span '(.+?)' with parent '(.+?)'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Dashboard/Otlp/Model/OtlpTrace.cs","lineNumber":61,"sourceCode":"        {\n            throw new InvalidOperationException($\"Duplicate span id '{span.SpanId}' detected.\");\n        }\n\n        var insertIndex = 0;\n        for (var i = Spans.Count - 1; i >= 0; i--)\n        {\n            if (span.StartTime > Spans[i].StartTime)\n            {\n                insertIndex = i + 1;\n                break;\n            }\n        }\n        Spans.Insert(insertIndex, span);\n\n        if (HasCircularReference(span))\n        {\n            Spans.Remove(span);\n            throw new InvalidOperationException($\"Circular loop detected for span '{span.SpanId}' with parent '{span.ParentSpanId}'.\");\n        }\n\n        _duration = null;\n\n        if (string.IsNullOrEmpty(span.ParentSpanId))\n        {\n            // There should only be one span with no parent span ID.\n            // Incase there isn't, the first span with no parent span ID is considered to be the root.\n            foreach (var existingSpan in Spans)\n            {\n                if (string.IsNullOrEmpty(existingSpan.ParentSpanId))\n                {\n                    _rootSpan = existingSpan;\n                    FullName = BuildFullName(existingSpan);\n                    break;\n                }\n            }\n        }","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Dashboard/Otlp/Model/OtlpTrace.cs#L43-L79","documentation":"OtlpTrace.AddSpan validates parent/child relationships and throws this InvalidOperationException when a newly inserted span creates a circular parent chain (a span is its own ancestor). Circular references would cause infinite loops or broken tree rendering, so the span is removed from Spans and the error is thrown. This guards against malformed trace data where ParentSpanId points back into the span's own ancestry.","triggerScenarios":"AddSpan throws when HasCircularReference(span) is true after inserting the span, i.e. span.ParentSpanId resolves (directly or transitively) back to span.SpanId. Caused by telemetry producers emitting inconsistent parent ids, e.g. a span listing itself as parent, or A/B cycles from buggy context propagation.","commonSituations":"Buggy custom context propagation overwriting parent ids; span re-parenting logic in middleware producing a cycle; test fixtures hand-crafting span graphs with wrong parent ids; producer bugs after sampling/context corruption in async flows.","solutions":["Fix the emitting instrumentation so ParentSpanId always refers to an ancestor, never the span itself or its descendant.","Validate span graphs before import: detect and break cycles by nulling the offending ParentSpanId.","If the producer is a custom exporter/library, verify it copies the correct parent context (Activity.ParentSpanId / parent trace flags) when creating spans."],"exampleFix":"// before: self-referential parent\nvar span = new OtlpSpan { SpanId = id, ParentSpanId = id };\n// after: parent must be a different, pre-existing span (or empty for a root)\nvar span = new OtlpSpan { SpanId = id, ParentSpanId = parentId != id ? parentId : null };","handlingStrategy":"validation","validationCode":"// verify the parent chain is acyclic before adding\nbool isSelfParent = span.SpanId == span.ParentSpanId;\nbool parentExists = string.IsNullOrEmpty(span.ParentSpanId) || trace.Spans.Any(s => s.SpanId == span.ParentSpanId);","typeGuard":null,"tryCatchPattern":"try\n{\n    trace.AddSpan(span);\n}\ncatch (InvalidOperationException ex) when (ex.Message.StartsWith(\"Circular loop detected\"))\n{\n    logger.LogWarning(ex, \"Rejecting span {SpanId} with cyclic parent {ParentId}.\", span.SpanId, span.ParentSpanId);\n}","preventionTips":["Never set a span's ParentSpanId to its own SpanId or to one of its descendants.","Verify context propagation code copies the correct parent id in async/ambient flows.","Validate span graphs in fixtures before feeding them into AddSpan."],"tags":["opentelemetry","traces","circular-reference","span","otlp"],"backgroundTag":"circular-reference-detected","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"}