{"record":{"id":"fd7e04f27852d5d6","repo":"microsoft/aspire","slug":"the-launch-configuration-callback-context-belongs-to","errorCode":null,"errorMessage":"The launch configuration callback context belongs to resource '{context.Resource.Name}', but launch configuration was requested for resource '{resource.Name}'.","messagePattern":"The launch configuration callback context belongs to resource '(.+?)', but launch configuration was requested for resource '(.+?)'\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/ApplicationModel/DebugSupportExtensions.cs","lineNumber":173,"sourceCode":"    /// <para>\n    /// This method never resolves environment variables. Aspire creates <paramref name=\"context\"/>\n    /// when the active debug-support annotation is producing a launch configuration for an executable creation.\n    /// </para>\n    /// <para>\n    /// This overload is internal because only Aspire constructs callback contexts containing resolved environment\n    /// variables. Use the public overload when inspecting a launch configuration outside executable creation.\n    /// </para>\n    /// </remarks>\n    internal static Task<object> CreateLaunchConfigurationAsync(\n        this IResource resource,\n        LaunchConfigurationCallbackContext context)\n    {\n        ArgumentNullException.ThrowIfNull(resource);\n        ArgumentNullException.ThrowIfNull(context);\n\n        if (!ReferenceEquals(resource, context.Resource))\n        {\n            throw new ArgumentException(\n                $\"The launch configuration callback context belongs to resource '{context.Resource.Name}', \" +\n                $\"but launch configuration was requested for resource '{resource.Name}'.\",\n                nameof(context));\n        }\n\n        if (!resource.TryGetLastAnnotation<SupportsDebuggingAnnotation>(out var supportsDebuggingAnnotation))\n        {\n            throw new InvalidOperationException(\n                $\"Resource '{resource.Name}' does not declare debug launch support. \" +\n                $\"Call {nameof(ResourceBuilderExtensions.WithDebugSupport)} on the resource first. \" +\n                $\"Note that it only adds the annotation in run mode.\");\n        }\n\n        return supportsDebuggingAnnotation.LaunchConfigurationProducer(context);\n    }\n\n    private static string[]? GetSupportedLaunchConfigurations(IConfiguration configuration)\n    {","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/ApplicationModel/DebugSupportExtensions.cs#L155-L191","documentation":"CreateLaunchConfigurationAsync validates that the resource passed to it is the exact same instance as the resource carried by the LaunchConfigurationCallbackContext, since the context's callbacks and environment were built for that specific resource. When they differ, it throws ArgumentException on the context parameter naming both resources. This catches mismatches early instead of producing launch configuration for the wrong resource.","triggerScenarios":"Calling CreateLaunchConfigurationAsync(context, resource) where resource is a different resource object than context.Resource — e.g. iterating over one collection of resources while holding contexts created for another, or re-creating resource instances so reference equality fails.","commonSituations":"Custom orchestrators or test harnesses wiring launch contexts to a rebuilt/cloned resource model; storing contexts in a dictionary keyed by name and passing the wrong entry; after hot reload or model regeneration where the resource instance was replaced.","solutions":["Pass the exact resource instance stored in context.Resource (pass context.Resource itself, or look up the matching context for the resource).","Key contexts by resource instance, not by name, to avoid mismatched pairs.","Recreate the callback context for the new resource instance if the model changed."],"exampleFix":"// before\nforeach (var r in model.Resources)\n    var cfg = await context.Resource.CreateLaunchConfigurationAsync(context); // wrong pairing\n// after\nvar cfg = await resource.CreateLaunchConfigurationAsync(context); // context.Resource == resource","handlingStrategy":"validation","validationCode":"if (!ReferenceEquals(resource, context.Resource))\n    throw new InvalidOperationException($\"Context belongs to '{context.Resource.Name}' but was paired with '{resource.Name}'.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    var cfg = await resource.CreateLaunchConfigurationAsync(context, ct);\n}\ncatch (ArgumentException ex) when (ex.ParamName == nameof(context))\n{\n    logger.LogError(ex, \"Launch context/resource mismatch: {Message}\", ex.Message);\n    throw;\n}","preventionTips":["Always pass context.Resource (or look up the context keyed by the resource instance) — never a different resource.","Key launch contexts by resource instance (reference equality) rather than by name.","Rebuild contexts after any model regeneration instead of reusing them for new instances.","Add a debug assert for ReferenceEquals(resource, context.Resource) in orchestrator code."],"tags":["dotnet","aspire","debugging","launch-configuration"],"backgroundTag":"invalid-argument-value","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"}